From 1c54758ad6b1279500921dd2661289f4f23ef44e Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 11 May 2017 16:22:36 +0200 Subject: [PATCH 1/9] Introduce style::values::generics::image --- components/layout/display_list_builder.rs | 25 +- components/style/gecko/conversions.rs | 13 +- components/style/values/computed/image.rs | 322 +---------------- components/style/values/generics/image.rs | 362 +++++++++++++++++++ components/style/values/generics/mod.rs | 1 + components/style/values/specified/image.rs | 162 ++------- ports/geckolib/glue.rs | 2 +- tests/unit/style/properties/serialization.rs | 2 +- 8 files changed, 429 insertions(+), 460 deletions(-) create mode 100644 components/style/values/generics/image.rs diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 5f9a783c896..5ecae6d66de 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -57,11 +57,12 @@ use style::properties::{self, ServoComputedValues}; use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword; use style::properties::style_structs; use style::servo::restyle_damage::REPAINT; -use style::values::{Either, RGBA, computed}; +use style::values::{Either, RGBA}; use style::values::computed::{AngleOrCorner, Gradient, GradientItem, GradientKind, LengthOrPercentage}; use style::values::computed::{LengthOrPercentageOrAuto, LengthOrKeyword, LengthOrPercentageOrKeyword}; use style::values::computed::{NumberOrPercentage, Position}; use style::values::computed::image::{EndingShape, SizeKeyword}; +use style::values::generics::image::{GradientItem as GenericGradientItem, Image}; use style::values::specified::{HorizontalDirection, VerticalDirection}; use style_traits::CSSPixel; use style_traits::cursor::Cursor; @@ -610,7 +611,7 @@ fn convert_gradient_stops(gradient_items: &[GradientItem], // Only keep the color stops, discard the color interpolation hints. let mut stop_items = gradient_items.iter().filter_map(|item| { match *item { - GradientItem::ColorStop(ref stop) => Some(*stop), + GenericGradientItem::ColorStop(ref stop) => Some(*stop), _ => None, } }).collect::>(); @@ -855,7 +856,7 @@ impl FragmentDisplayListBuilding for Fragment { for (i, background_image) in background.background_image.0.iter().enumerate().rev() { match background_image.0 { None => {} - Some(computed::Image::Gradient(ref gradient)) => { + Some(Image::Gradient(ref gradient)) => { self.build_display_list_for_background_gradient(state, display_list_section, &absolute_bounds, @@ -864,7 +865,7 @@ impl FragmentDisplayListBuilding for Fragment { gradient, style); } - Some(computed::Image::Url(ref image_url)) => { + Some(Image::Url(ref image_url)) => { if let Some(url) = image_url.url() { self.build_display_list_for_background_image(state, style, @@ -875,10 +876,10 @@ impl FragmentDisplayListBuilding for Fragment { i); } } - Some(computed::Image::ImageRect(_)) => { + Some(Image::Rect(_)) => { // TODO: Implement `-moz-image-rect` } - Some(computed::Image::Element(_)) => { + Some(Image::Element(_)) => { // TODO: Implement `-moz-element` } } @@ -1221,7 +1222,7 @@ impl FragmentDisplayListBuilding for Fragment { style.get_cursor(Cursor::Default), display_list_section); - let display_item = match gradient.gradient_kind { + let display_item = match gradient.kind { GradientKind::Linear(ref angle_or_corner) => { let gradient = self.convert_linear_gradient(&bounds, &gradient.items[..], @@ -1357,8 +1358,8 @@ impl FragmentDisplayListBuilding for Fragment { }), })); } - Some(computed::Image::Gradient(ref gradient)) => { - match gradient.gradient_kind { + Some(Image::Gradient(ref gradient)) => { + match gradient.kind { GradientKind::Linear(angle_or_corner) => { let grad = self.convert_linear_gradient(&bounds, &gradient.items[..], @@ -1398,13 +1399,13 @@ impl FragmentDisplayListBuilding for Fragment { } } } - Some(computed::Image::ImageRect(..)) => { + Some(Image::Rect(..)) => { // TODO: Handle border-image with `-moz-image-rect`. } - Some(computed::Image::Element(..)) => { + Some(Image::Element(..)) => { // TODO: Handle border-image with `-moz-element`. } - Some(computed::Image::Url(ref image_url)) => { + Some(Image::Url(ref image_url)) => { if let Some(url) = image_url.url() { let webrender_image = state.layout_context .get_webrender_image_for_url(self.node, diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index bb299a74db1..d3d74234c35 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -16,8 +16,9 @@ use gecko_bindings::structs::{nsCSSUnit, nsStyleCoord_CalcValue, nsStyleImage}; use gecko_bindings::structs::{nsresult, SheetType}; use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut}; use stylesheets::{Origin, RulesMutateError}; -use values::computed::{Angle, CalcLengthOrPercentage, Gradient, GradientItem, Image}; +use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image}; use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; +use values::generics::image::{GradientItem, Image as GenericImage}; impl From for nsStyleCoord_CalcValue { fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue { @@ -138,10 +139,10 @@ impl nsStyleImage { /// Set a given Servo `Image` value into this `nsStyleImage`. pub fn set(&mut self, image: Image, cacheable: &mut bool) { match image { - Image::Gradient(gradient) => { + GenericImage::Gradient(gradient) => { self.set_gradient(gradient) }, - Image::Url(ref url) => { + GenericImage::Url(ref url) => { unsafe { Gecko_SetUrlImageValue(self, url.for_ffi()); // We unfortunately must make any url() value uncacheable, since @@ -154,7 +155,7 @@ impl nsStyleImage { *cacheable = false; } }, - Image::ImageRect(ref image_rect) => { + GenericImage::Rect(ref image_rect) => { unsafe { Gecko_SetUrlImageValue(self, image_rect.url.for_ffi()); Gecko_InitializeImageCropRect(self); @@ -176,7 +177,7 @@ impl nsStyleImage { image_rect.left.to_gecko_style_coord(&mut rect.data_at_mut(3)); } } - Image::Element(ref element) => { + GenericImage::Element(ref element) => { unsafe { Gecko_SetImageElement(self, element.as_ptr()); } @@ -201,7 +202,7 @@ impl nsStyleImage { return; } - let gecko_gradient = match gradient.gradient_kind { + let gecko_gradient = match gradient.kind { GradientKind::Linear(angle_or_corner) => { let gecko_gradient = unsafe { Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8, diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 01a6106f3d3..5140650d80e 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -7,128 +7,35 @@ //! //! [image]: https://drafts.csswg.org/css-images/#image-values -use Atom; -use cssparser::{Color as CSSColor, serialize_identifier}; +use cssparser::Color as CSSColor; use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; +use values::generics::image::{CompatMode, ColorStop as GenericColorStop}; +use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; +use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect}; use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue}; use values::computed::position::Position; use values::specified::{self, HorizontalDirection, VerticalDirection}; -use values::specified::image::CompatMode; -use values::specified::url::SpecifiedUrl; pub use values::specified::SizeKeyword; -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)) - }, - specified::Image::ImageRect(ref image_rect) => { - Image::ImageRect(image_rect.to_computed_value(context)) - }, - specified::Image::Element(ref selector) => { - Image::Element(selector.clone()) - } - } - } - - #[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) - ) - }, - Image::ImageRect(ref image_rect) => { - specified::Image::ImageRect( - ToComputedValue::from_computed_value(image_rect) - ) - }, - Image::Element(ref selector) => { - specified::Image::Element(selector.clone()) - }, - } - } -} - /// 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), - ImageRect(ImageRect), - Element(Atom), -} - -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), - } - }, - Image::ImageRect(ref image_rect) => write!(f, "{:?}", image_rect), - Image::Element(ref selector) => { - f.write_str("-moz-element(#")?; - serialize_identifier(&*selector.to_string(), f)?; - f.write_str(")") - }, - } - } -} - -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), - Image::ImageRect(ref image_rect) => image_rect.to_css(dest), - Image::Element(ref selector) => { - dest.write_str("-moz-element(#")?; - // FIXME: We should get rid of these intermediate strings. - serialize_identifier(&*selector.to_string(), dest)?; - dest.write_str(")") - }, - } - } -} +pub type Image = GenericImage; /// 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 items: Vec, - /// True if this is a repeating gradient. - pub repeating: bool, - /// Gradient kind can be linear or radial. - pub gradient_kind: GradientKind, - /// Compatibility mode. - pub compat_mode: CompatMode, -} +pub type Gradient = GenericGradient; + +/// A computed gradient item. +pub type GradientItem = GenericGradientItem; + +/// A computed color stop. +pub type ColorStop = GenericColorStop; + +/// Computed values for ImageRect. +pub type ImageRect = GenericImageRect; impl ToCss for Gradient { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { @@ -138,7 +45,7 @@ impl ToCss for Gradient { if self.repeating { try!(dest.write_str("repeating-")); } - match self.gradient_kind { + match self.kind { GradientKind::Linear(angle_or_corner) => { try!(dest.write_str("linear-gradient(")); try!(angle_or_corner.to_css(dest, self.compat_mode)); @@ -161,7 +68,7 @@ impl ToCss for Gradient { impl fmt::Debug for Gradient { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.gradient_kind { + match self.kind { GradientKind::Linear(angle_or_corner) => { let _ = write!(f, "{:?}", angle_or_corner); }, @@ -177,42 +84,6 @@ impl fmt::Debug for Gradient { } } -impl ToComputedValue for specified::Gradient { - type ComputedValue = Gradient; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Gradient { - let specified::Gradient { - ref items, - repeating, - ref gradient_kind, - compat_mode, - } = *self; - Gradient { - items: items.iter().map(|s| s.to_computed_value(context)).collect(), - repeating: repeating, - gradient_kind: gradient_kind.to_computed_value(context), - compat_mode: compat_mode, - } - } - - #[inline] - fn from_computed_value(computed: &Gradient) -> Self { - let Gradient { - ref items, - repeating, - ref gradient_kind, - compat_mode, - } = *computed; - specified::Gradient { - items: items.iter().map(ToComputedValue::from_computed_value).collect(), - repeating: repeating, - gradient_kind: ToComputedValue::from_computed_value(gradient_kind), - compat_mode: compat_mode, - } - } -} - /// Computed values for CSS linear or radial gradients. /// https://drafts.csswg.org/css-images/#gradients #[derive(Clone, PartialEq)] @@ -252,112 +123,6 @@ impl ToComputedValue for specified::GradientKind { } } -/// Specified values for color stops and interpolation hints. -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum GradientItem { - /// A color stop. - ColorStop(ColorStop), - /// An interpolation hint. - InterpolationHint(LengthOrPercentage), -} - -impl ToCss for GradientItem { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - GradientItem::ColorStop(stop) => stop.to_css(dest), - GradientItem::InterpolationHint(hint) => hint.to_css(dest), - } - } -} - -impl ToComputedValue for specified::GradientItem { - type ComputedValue = GradientItem; - - #[inline] - fn to_computed_value(&self, context: &Context) -> GradientItem { - match *self { - specified::GradientItem::ColorStop(ref stop) => { - GradientItem::ColorStop(stop.to_computed_value(context)) - }, - specified::GradientItem::InterpolationHint(ref hint) => { - GradientItem::InterpolationHint(hint.to_computed_value(context)) - }, - } - } - - #[inline] - fn from_computed_value(computed: &GradientItem) -> Self { - match *computed { - GradientItem::ColorStop(ref stop) => { - specified::GradientItem::ColorStop(ToComputedValue::from_computed_value(stop)) - }, - GradientItem::InterpolationHint(ref hint) => { - specified::GradientItem::InterpolationHint(ToComputedValue::from_computed_value(hint)) - }, - } - } -} - -/// 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.to_computed_value(context), - 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)] @@ -425,59 +190,6 @@ impl ToComputedValue for specified::GradientEndingShape { } } -/// Computed values for ImageRect -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub struct ImageRect { - pub url: SpecifiedUrl, - pub top: NumberOrPercentage, - pub bottom: NumberOrPercentage, - pub right: NumberOrPercentage, - pub left: NumberOrPercentage, -} - -impl ToCss for ImageRect { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - dest.write_str("-moz-image-rect(")?; - self.url.to_css(dest)?; - dest.write_str(", ")?; - self.top.to_css(dest)?; - dest.write_str(", ")?; - self.right.to_css(dest)?; - dest.write_str(", ")?; - self.bottom.to_css(dest)?; - dest.write_str(", ")?; - self.left.to_css(dest)?; - dest.write_str(")") - } -} - -impl ToComputedValue for specified::ImageRect { - type ComputedValue = ImageRect; - - #[inline] - fn to_computed_value(&self, context: &Context) -> ImageRect { - ImageRect { - url: self.url.to_computed_value(context), - top: self.top.to_computed_value(context), - right: self.right.to_computed_value(context), - bottom: self.bottom.to_computed_value(context), - left: self.left.to_computed_value(context), - } - } - #[inline] - fn from_computed_value(computed: &ImageRect) -> Self { - specified::ImageRect { - url: ToComputedValue::from_computed_value(&computed.url), - top: ToComputedValue::from_computed_value(&computed.top), - right: ToComputedValue::from_computed_value(&computed.right), - bottom: ToComputedValue::from_computed_value(&computed.bottom), - left: ToComputedValue::from_computed_value(&computed.left), - } - } -} - /// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size #[derive(Clone, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs new file mode 100644 index 00000000000..de644215acc --- /dev/null +++ b/components/style/values/generics/image.rs @@ -0,0 +1,362 @@ +/* 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/. */ + +//! Generic types for the handling of [images]. +//! +//! [images]: https://drafts.csswg.org/css-images/#image-values + +use Atom; +use cssparser::serialize_identifier; +use std::fmt; +use style_traits::ToCss; +use values::HasViewportPercentage; +use values::computed::{Context, ToComputedValue}; +use values::specified::url::SpecifiedUrl; + +/// An [image]. +/// +/// [image]: https://drafts.csswg.org/css-images/#image-values +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Image { + /// A `` image. + Url(SpecifiedUrl), + /// A `` image. + Gradient(G), + /// A `-moz-image-rect` image + Rect(ImageRect), + /// A `-moz-element(# )` + Element(Atom), +} + +/// A CSS gradient. +/// https://drafts.csswg.org/css-images/#gradients +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct Gradient { + /// Gradients can be linear or radial. + pub kind: K, + /// The color stops and interpolation hints. + pub items: Vec>, + /// True if this is a repeating gradient. + pub repeating: bool, + /// Compatibility mode. + pub compat_mode: CompatMode, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +/// Whether we used the modern notation or the compatibility `-webkit` prefix. +pub enum CompatMode { + /// Modern syntax. + Modern, + /// `-webkit` prefix. + WebKit, +} + +/// A gradient item. +/// https://drafts.csswg.org/css-images-4/#color-stop-syntax +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum GradientItem { + /// A color stop. + ColorStop(ColorStop), + /// An interpolation hint. + InterpolationHint(L), +} + +/// A color stop. +/// https://drafts.csswg.org/css-images/#typedef-color-stop-list +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct ColorStop { + /// The color of this stop. + pub color: C, + /// The position of this stop. + pub position: Option, +} + +/// Values for `moz-image-rect`. +/// +/// `-moz-image-rect(, top, right, bottom, left);` +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[allow(missing_docs)] +pub struct ImageRect { + pub url: SpecifiedUrl, + pub top: C, + pub bottom: C, + pub right: C, + pub left: C, +} + +impl fmt::Debug for Image + where G: fmt::Debug, N: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Image::Url(ref url) => url.to_css(f), + Image::Gradient(ref grad) => grad.fmt(f), + Image::Rect(ref rect) => rect.fmt(f), + Image::Element(ref selector) => { + f.write_str("-moz-element(#")?; + serialize_identifier(&selector.to_string(), f)?; + f.write_str(")") + }, + } + } +} + +impl ToCss for Image + where G: ToCss, N: ToCss, +{ + 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), + Image::Rect(ref rect) => rect.to_css(dest), + Image::Element(ref selector) => { + dest.write_str("-moz-element(#")?; + serialize_identifier(&selector.to_string(), dest)?; + dest.write_str(")") + }, + } + } +} + +impl HasViewportPercentage for Image + where G: HasViewportPercentage +{ + fn has_viewport_percentage(&self) -> bool { + match *self { + Image::Gradient(ref gradient) => gradient.has_viewport_percentage(), + _ => false, + } + } +} + +impl ToComputedValue for Image + where G: ToComputedValue, N: ToComputedValue, +{ + type ComputedValue = Image<::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + Image::Url(ref url) => { + Image::Url(url.clone()) + }, + Image::Gradient(ref gradient) => { + Image::Gradient(gradient.to_computed_value(context)) + }, + Image::Rect(ref rect) => { + Image::Rect(rect.to_computed_value(context)) + }, + Image::Element(ref selector) => { + Image::Element(selector.clone()) + } + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + Image::Url(ref url) => { + Image::Url(url.clone()) + }, + Image::Gradient(ref gradient) => { + Image::Gradient(ToComputedValue::from_computed_value(gradient)) + }, + Image::Rect(ref rect) => { + Image::Rect(ToComputedValue::from_computed_value(rect)) + }, + Image::Element(ref selector) => { + Image::Element(selector.clone()) + }, + } + } +} + +impl HasViewportPercentage for Gradient + where K: HasViewportPercentage, L: HasViewportPercentage, +{ + fn has_viewport_percentage(&self) -> bool { + self.kind.has_viewport_percentage() || + self.items.iter().any(|i| i.has_viewport_percentage()) + } +} + +impl ToComputedValue for Gradient + where K: ToComputedValue, C: ToComputedValue, L: ToComputedValue, +{ + type ComputedValue = Gradient<::ComputedValue, + ::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + Gradient { + kind: self.kind.to_computed_value(context), + items: self.items.iter().map(|s| s.to_computed_value(context)).collect(), + repeating: self.repeating, + compat_mode: self.compat_mode, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + Gradient { + kind: ToComputedValue::from_computed_value(&computed.kind), + items: computed.items.iter().map(ToComputedValue::from_computed_value).collect(), + repeating: computed.repeating, + compat_mode: computed.compat_mode, + } + } +} + +impl ToCss for GradientItem + where C: ToCss, L: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + GradientItem::ColorStop(ref stop) => stop.to_css(dest), + GradientItem::InterpolationHint(ref hint) => hint.to_css(dest), + } + } +} + +impl HasViewportPercentage for GradientItem + where L: HasViewportPercentage, +{ + fn has_viewport_percentage(&self) -> bool { + match *self { + GradientItem::ColorStop(ref stop) => stop.has_viewport_percentage(), + GradientItem::InterpolationHint(ref hint) => hint.has_viewport_percentage(), + } + } +} + +impl ToComputedValue for GradientItem + where C: ToComputedValue, L: ToComputedValue, +{ + type ComputedValue = GradientItem<::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + GradientItem::ColorStop(ref stop) => { + GradientItem::ColorStop(stop.to_computed_value(context)) + }, + GradientItem::InterpolationHint(ref hint) => { + GradientItem::InterpolationHint(hint.to_computed_value(context)) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + GradientItem::ColorStop(ref stop) => { + GradientItem::ColorStop(ToComputedValue::from_computed_value(stop)) + }, + GradientItem::InterpolationHint(ref hint) => { + GradientItem::InterpolationHint(ToComputedValue::from_computed_value(hint)) + }, + } + } +} + +impl fmt::Debug for ColorStop + where C: fmt::Debug, L: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.color)?; + if let Some(ref pos) = self.position { + write!(f, " {:?}", pos)?; + } + Ok(()) + } +} + +impl ToCss for ColorStop + where C: ToCss, L: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.color.to_css(dest)?; + if let Some(ref position) = self.position { + dest.write_str(" ")?; + position.to_css(dest)?; + } + Ok(()) + } +} + +impl HasViewportPercentage for ColorStop + where L: HasViewportPercentage, +{ + fn has_viewport_percentage(&self) -> bool { + self.position.as_ref().map_or(false, HasViewportPercentage::has_viewport_percentage) + } +} + +impl ToComputedValue for ColorStop + where C: ToComputedValue, L: ToComputedValue, +{ + type ComputedValue = ColorStop<::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + ColorStop { + color: self.color.to_computed_value(context), + position: self.position.as_ref().map(|p| p.to_computed_value(context)), + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + ColorStop { + color: ToComputedValue::from_computed_value(&computed.color), + position: computed.position.as_ref().map(ToComputedValue::from_computed_value), + } + } +} + +impl ToCss for ImageRect + where C: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str("-moz-image-rect(")?; + self.url.to_css(dest)?; + dest.write_str(", ")?; + self.top.to_css(dest)?; + dest.write_str(", ")?; + self.right.to_css(dest)?; + dest.write_str(", ")?; + self.bottom.to_css(dest)?; + dest.write_str(", ")?; + self.left.to_css(dest)?; + dest.write_str(")") + } +} + +impl ToComputedValue for ImageRect + where C: ToComputedValue, +{ + type ComputedValue = ImageRect<::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + ImageRect { + url: self.url.clone(), + top: self.top.to_computed_value(context), + right: self.right.to_computed_value(context), + bottom: self.bottom.to_computed_value(context), + left: self.left.to_computed_value(context), + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + ImageRect { + url: computed.url.clone(), + top: ToComputedValue::from_computed_value(&computed.top), + right: ToComputedValue::from_computed_value(&computed.right), + bottom: ToComputedValue::from_computed_value(&computed.bottom), + left: ToComputedValue::from_computed_value(&computed.left), + } + } +} diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 43cf722fb7e..604d6e21238 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -14,6 +14,7 @@ use super::computed::{Context, ToComputedValue}; pub use self::basic_shape::serialize_radius_values; pub mod basic_shape; +pub mod image; pub mod position; #[derive(Clone, PartialEq, Debug)] diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 515082d2c11..62e971782f9 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -8,68 +8,58 @@ //! [image]: https://drafts.csswg.org/css-images/#image-values use Atom; -use cssparser::{Parser, Token, serialize_identifier}; +use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; #[cfg(feature = "servo")] use servo_url::ServoUrl; use std::fmt; use style_traits::ToCss; +use values::generics::image::{CompatMode, ColorStop as GenericColorStop}; +use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; +use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect}; use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage}; use values::specified::position::Position; use values::specified::url::SpecifiedUrl; /// 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 { - /// A `` image. - Url(SpecifiedUrl), - /// A `` image. - Gradient(Gradient), - /// A `-moz-image-rect` image - ImageRect(ImageRect), - /// A `-moz-element(# )` - Element(Atom), -} +pub type Image = GenericImage; -impl ToCss for Image { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - Image::Url(ref url_value) => url_value.to_css(dest), - Image::Gradient(ref gradient) => gradient.to_css(dest), - Image::ImageRect(ref image_rect) => image_rect.to_css(dest), - Image::Element(ref selector) => { - dest.write_str("-moz-element(#")?; - // FIXME: We should get rid of these intermediate strings. - serialize_identifier(&*selector.to_string(), dest)?; - dest.write_str(")") - }, - } - } -} +/// Specified values for a CSS gradient. +/// https://drafts.csswg.org/css-images/#gradients +pub type Gradient = GenericGradient; + +/// A specified gradient item. +pub type GradientItem = GenericGradientItem; + +/// A computed color stop. +pub type ColorStop = GenericColorStop; + +/// Specified values for `moz-image-rect` +/// -moz-image-rect(, top, right, bottom, left); +pub type ImageRect = GenericImageRect; impl Image { #[allow(missing_docs)] pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { if let Ok(url) = input.try(|input| SpecifiedUrl::parse(context, input)) { - return Ok(Image::Url(url)); + return Ok(GenericImage::Url(url)); } if let Ok(gradient) = input.try(|input| Gradient::parse_function(context, input)) { - return Ok(Image::Gradient(gradient)); + return Ok(GenericImage::Gradient(gradient)); } if let Ok(image_rect) = input.try(|input| ImageRect::parse(context, input)) { - return Ok(Image::ImageRect(image_rect)); + return Ok(GenericImage::Rect(image_rect)); } - Ok(Image::Element(Image::parse_element(input)?)) + Ok(GenericImage::Element(Image::parse_element(input)?)) } /// Creates an already specified image value from an already resolved URL /// for insertion in the cascade. #[cfg(feature = "servo")] pub fn for_cascade(url: ServoUrl) -> Self { - Image::Url(SpecifiedUrl::for_cascade(url)) + GenericImage::Url(SpecifiedUrl::for_cascade(url)) } /// Parses a `-moz-element(# )`. @@ -87,21 +77,6 @@ impl Image { } } -/// 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 and interpolation hints. - pub items: Vec, - /// True if this is a repeating gradient. - pub repeating: bool, - /// Gradients can be linear or radial. - pub gradient_kind: GradientKind, - /// Compatibility mode. - pub compat_mode: CompatMode, -} - impl ToCss for Gradient { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { if self.compat_mode == CompatMode::WebKit { @@ -111,7 +86,7 @@ impl ToCss for Gradient { try!(dest.write_str("repeating-")); } let mut skipcomma = false; - match self.gradient_kind { + match self.kind { GradientKind::Linear(angle_or_corner) => { try!(dest.write_str("linear-gradient(")); try!(angle_or_corner.to_css(dest, self.compat_mode)); @@ -198,7 +173,7 @@ impl Gradient { Ok(Gradient { items: items, repeating: repeating, - gradient_kind: gradient_kind, + kind: gradient_kind, compat_mode: compat_mode, }) } @@ -209,11 +184,11 @@ impl Gradient { if seen_stop { if let Ok(hint) = input.try(|i| LengthOrPercentage::parse(context, i)) { seen_stop = false; - return Ok(GradientItem::InterpolationHint(hint)); + return Ok(GenericGradientItem::InterpolationHint(hint)); } } seen_stop = true; - ColorStop::parse(context, input).map(GradientItem::ColorStop) + ColorStop::parse(context, input).map(GenericGradientItem::ColorStop) })); if !seen_stop || items.len() < 2 { return Err(()); @@ -238,16 +213,6 @@ pub enum GradientKind { Radial(EndingShape, Position), } -#[derive(Clone, Copy, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -/// Whether we used the modern notation or the compatibility `-webkit` prefix. -pub enum CompatMode { - /// Modern syntax. - Modern, - /// `-webkit` prefix. - WebKit, -} - impl GradientKind { /// Parses a linear gradient kind from the given arguments. fn parse_modern_linear(context: &ParserContext, input: &mut Parser) -> Result { @@ -396,35 +361,6 @@ impl GradientKind { } } -/// Specified values for `moz-image-rect` -/// -moz-image-rect(, top, right, bottom, left); -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub struct ImageRect { - pub url: SpecifiedUrl, - pub top: NumberOrPercentage, - pub bottom: NumberOrPercentage, - pub right: NumberOrPercentage, - pub left: NumberOrPercentage, -} - -impl ToCss for ImageRect { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - dest.write_str("-moz-image-rect(")?; - self.url.to_css(dest)?; - dest.write_str(", ")?; - self.top.to_css(dest)?; - dest.write_str(", ")?; - self.right.to_css(dest)?; - dest.write_str(", ")?; - self.bottom.to_css(dest)?; - dest.write_str(", ")?; - self.left.to_css(dest)?; - dest.write_str(")") - } -} - impl Parse for ImageRect { fn parse(context: &ParserContext, input: &mut Parser) -> Result { match_ignore_ascii_case! { &try!(input.expect_function()), @@ -502,50 +438,6 @@ impl AngleOrCorner { } } -/// Specified values for color stops and interpolation hints. -/// https://drafts.csswg.org/css-images-4/#color-stop-syntax -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum GradientItem { - /// A color stop. - ColorStop(ColorStop), - /// An interpolation hint. - InterpolationHint(LengthOrPercentage), -} - -impl ToCss for GradientItem { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - GradientItem::ColorStop(ref stop) => stop.to_css(dest), - GradientItem::InterpolationHint(ref hint) => hint.to_css(dest), - } - } -} - -/// Specified values for one color stop in a 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(ref 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); diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 0b209849aae..822f34056e4 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -1801,7 +1801,7 @@ pub extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(declarations: use style::properties::PropertyDeclaration; use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage; use style::properties::longhands::background_image::single_value::SpecifiedValue as SingleBackgroundImage; - use style::values::specified::image::Image; + use style::values::generics::image::Image; use style::values::specified::url::SpecifiedUrl; let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) }; diff --git a/tests/unit/style/properties/serialization.rs b/tests/unit/style/properties/serialization.rs index 426597e56b4..d784454f712 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -799,7 +799,7 @@ mod shorthand_serialization { use style::properties::longhands::mask_position_y as position_y; use style::properties::longhands::mask_repeat as repeat; use style::properties::longhands::mask_size as size; - use style::values::specified::Image; + use style::values::generics::image::Image; use super::*; macro_rules! single_vec_value_typedef { From f2aaba685b81d7cf8311bbe791681d1ab55cfa78 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 11 May 2017 17:39:09 +0200 Subject: [PATCH 2/9] Kill side keywords from style::values::specified::image --- components/layout/display_list_builder.rs | 10 ++++----- components/style/gecko/conversions.rs | 11 +++++----- components/style/values/computed/image.rs | 13 ++++++------ components/style/values/specified/image.rs | 23 +++++++++------------ components/style/values/specified/length.rs | 3 +-- components/style/values/specified/mod.rs | 4 ++-- 6 files changed, 31 insertions(+), 33 deletions(-) diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 5ecae6d66de..14bffb0a1f6 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -63,7 +63,7 @@ use style::values::computed::{LengthOrPercentageOrAuto, LengthOrKeyword, LengthO use style::values::computed::{NumberOrPercentage, Position}; use style::values::computed::image::{EndingShape, SizeKeyword}; use style::values::generics::image::{GradientItem as GenericGradientItem, Image}; -use style::values::specified::{HorizontalDirection, VerticalDirection}; +use style::values::specified::position::{X, Y}; use style_traits::CSSPixel; use style_traits::cursor::Cursor; use table_cell::CollapsedBordersForCell; @@ -1111,13 +1111,13 @@ impl FragmentDisplayListBuilding for Fragment { let atan = (bounds.size.height.to_f32_px() / bounds.size.width.to_f32_px()).atan(); match (horizontal, vertical) { - (HorizontalDirection::Right, VerticalDirection::Bottom) + (X::Right, Y::Bottom) => f32::consts::PI - atan, - (HorizontalDirection::Left, VerticalDirection::Bottom) + (X::Left, Y::Bottom) => f32::consts::PI + atan, - (HorizontalDirection::Right, VerticalDirection::Top) + (X::Right, Y::Top) => atan, - (HorizontalDirection::Left, VerticalDirection::Top) + (X::Left, Y::Top) => -atan, } } diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index d3d74234c35..7dc333964e4 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -194,7 +194,8 @@ impl nsStyleImage { use gecko_bindings::structs::nsStyleCoord; use values::computed::{AngleOrCorner, GradientKind, GradientShape, LengthOrKeyword}; use values::computed::LengthOrPercentageOrKeyword; - use values::specified::{HorizontalDirection, SizeKeyword, VerticalDirection}; + use values::specified::SizeKeyword; + use values::specified::position::{X, Y}; let stop_count = gradient.items.len(); if stop_count >= ::std::u32::MAX as usize { @@ -222,12 +223,12 @@ impl nsStyleImage { }, AngleOrCorner::Corner(horiz, vert) => { let percent_x = match horiz { - HorizontalDirection::Left => 0.0, - HorizontalDirection::Right => 1.0, + X::Left => 0.0, + X::Right => 1.0, }; let percent_y = match vert { - VerticalDirection::Top => 0.0, - VerticalDirection::Bottom => 1.0, + Y::Top => 0.0, + Y::Bottom => 1.0, }; unsafe { diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 5140650d80e..b31da88d338 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -16,7 +16,8 @@ use values::generics::image::{Gradient as GenericGradient, GradientItem as Gener use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect}; use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue}; use values::computed::position::Position; -use values::specified::{self, HorizontalDirection, VerticalDirection}; +use values::specified; +use values::specified::position::{X, Y}; pub use values::specified::SizeKeyword; @@ -320,7 +321,7 @@ impl ToComputedValue for specified::LengthOrPercentageOrKeyword { #[allow(missing_docs)] pub enum AngleOrCorner { Angle(Angle), - Corner(HorizontalDirection, VerticalDirection) + Corner(X, Y) } impl ToComputedValue for specified::AngleOrCorner { @@ -337,16 +338,16 @@ impl ToComputedValue for specified::AngleOrCorner { }, specified::AngleOrCorner::Corner(horizontal, vertical) => { match (horizontal, vertical) { - (None, Some(VerticalDirection::Top)) => { + (None, Some(Y::Top)) => { AngleOrCorner::Angle(Angle::from_radians(0.0)) }, - (Some(HorizontalDirection::Right), None) => { + (Some(X::Right), None) => { AngleOrCorner::Angle(Angle::from_radians(PI * 0.5)) }, - (None, Some(VerticalDirection::Bottom)) => { + (None, Some(Y::Bottom)) => { AngleOrCorner::Angle(Angle::from_radians(PI)) }, - (Some(HorizontalDirection::Left), None) => { + (Some(X::Left), None) => { AngleOrCorner::Angle(Angle::from_radians(PI * 1.5)) }, (Some(horizontal), Some(vertical)) => { diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 62e971782f9..01352fc187c 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -18,7 +18,7 @@ use values::generics::image::{CompatMode, ColorStop as GenericColorStop}; use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect}; use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage}; -use values::specified::position::Position; +use values::specified::position::{Position, X, Y}; use values::specified::url::SpecifiedUrl; /// Specified values for an image according to CSS-IMAGES. @@ -222,11 +222,11 @@ impl GradientKind { } else { if input.try(|i| i.expect_ident_matching("to")).is_ok() { let (horizontal, vertical) = - if let Ok(value) = input.try(HorizontalDirection::parse) { - (Some(value), input.try(VerticalDirection::parse).ok()) + if let Ok(value) = input.try(X::parse) { + (Some(value), input.try(Y::parse).ok()) } else { - let value = try!(VerticalDirection::parse(input)); - (input.try(HorizontalDirection::parse).ok(), Some(value)) + let value = try!(Y::parse(input)); + (input.try(X::parse).ok(), Some(value)) }; try!(input.expect_comma()); AngleOrCorner::Corner(horizontal, vertical) @@ -241,11 +241,11 @@ impl GradientKind { let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { AngleOrCorner::Angle(angle) } else { - if let Ok(value) = input.try(HorizontalDirection::parse) { - AngleOrCorner::Corner(Some(value), input.try(VerticalDirection::parse).ok()) + if let Ok(value) = input.try(X::parse) { + AngleOrCorner::Corner(Some(value), input.try(Y::parse).ok()) } else { - if let Ok(value) = input.try(VerticalDirection::parse) { - AngleOrCorner::Corner(input.try(HorizontalDirection::parse).ok(), Some(value)) + if let Ok(value) = input.try(Y::parse) { + AngleOrCorner::Corner(input.try(X::parse).ok(), Some(value)) } else { AngleOrCorner::None } @@ -408,7 +408,7 @@ fn parse_position(context: &ParserContext, input: &mut Parser) -> Result, Option), + Corner(Option, Option), None, } @@ -438,9 +438,6 @@ impl AngleOrCorner { } } -define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right); -define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom); - impl Parse for ColorStop { fn parse(context: &ParserContext, input: &mut Parser) -> Result { Ok(ColorStop { diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index 2062786adf8..13eb17b6a6f 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -25,8 +25,7 @@ use values::specified::calc::CalcNode; pub use values::specified::calc::CalcLengthOrPercentage; pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; -pub use super::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword}; -pub use super::image::{SizeKeyword, VerticalDirection}; +pub use super::image::{GradientKind, Image, LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword}; /// Number of app units per pixel pub const AU_PER_PX: CSSFloat = 60.; diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index aa8b791615b..d7245b25d2a 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -29,8 +29,8 @@ pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, Justify pub use self::color::Color; pub use self::grid::{GridLine, TrackKeyword}; pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; -pub use self::image::{GradientItem, GradientKind, HorizontalDirection, Image, ImageRect, LayerImage}; -pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword, VerticalDirection}; +pub use self::image::{GradientItem, GradientKind, Image, ImageRect, LayerImage}; +pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword}; pub use self::length::AbsoluteLength; pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage}; pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; From abef5da9d88a66df3856e9fb38e19932c277ed84 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 12 May 2017 17:28:05 +0200 Subject: [PATCH 3/9] Refactor Gradient In a similar way to Position, now specified and computed gradients share a common Gradient type defined in style::values::generics::image. This allows us to reuse most code for many style traits like ToCss, HasViewportPercentage and ToComputedValue. The test changes are the fallout of the disappearance of AngleOrCorner::None, which align our code to the spec for serialisation, where components that can be omitted should be omitted. --- components/layout/display_list_builder.rs | 77 ++- components/style/gecko/conversions.rs | 62 +- components/style/values/computed/image.rs | 426 +++---------- components/style/values/computed/length.rs | 3 +- components/style/values/computed/mod.rs | 3 +- components/style/values/generics/image.rs | 342 +++++++++-- components/style/values/specified/image.rs | 635 ++++++++------------ components/style/values/specified/length.rs | 4 +- components/style/values/specified/mod.rs | 3 +- tests/unit/style/parsing/image.rs | 75 +-- 10 files changed, 742 insertions(+), 888 deletions(-) diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 14bffb0a1f6..5f33702d693 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -58,11 +58,12 @@ use style::properties::longhands::border_image_repeat::computed_value::RepeatKey use style::properties::style_structs; use style::servo::restyle_damage::REPAINT; use style::values::{Either, RGBA}; -use style::values::computed::{AngleOrCorner, Gradient, GradientItem, GradientKind, LengthOrPercentage}; -use style::values::computed::{LengthOrPercentageOrAuto, LengthOrKeyword, LengthOrPercentageOrKeyword}; -use style::values::computed::{NumberOrPercentage, Position}; -use style::values::computed::image::{EndingShape, SizeKeyword}; -use style::values::generics::image::{GradientItem as GenericGradientItem, Image}; +use style::values::computed::{Gradient, GradientItem, LengthOrPercentage}; +use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position}; +use style::values::computed::image::{EndingShape, LineDirection}; +use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape}; +use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind}; +use style::values::generics::image::{Image, ShapeExtent}; use style::values::specified::position::{X, Y}; use style_traits::CSSPixel; use style_traits::cursor::Cursor; @@ -391,7 +392,7 @@ pub trait FragmentDisplayListBuilding { fn convert_linear_gradient(&self, bounds: &Rect, stops: &[GradientItem], - angle_or_corner: &AngleOrCorner, + direction: &LineDirection, repeating: bool, style: &ServoComputedValues) -> display_list::Gradient; @@ -759,36 +760,46 @@ fn get_ellipse_radius(size: &Size2D, center: &Point2D, cmp: F) -> Siz /// Determines the radius of a circle if it was not explictly provided. /// https://drafts.csswg.org/css-images-3/#typedef-size -fn convert_circle_size_keyword(keyword: SizeKeyword, +fn convert_circle_size_keyword(keyword: ShapeExtent, size: &Size2D, center: &Point2D) -> Size2D { - use style::values::computed::image::SizeKeyword::*; let radius = match keyword { - ClosestSide | Contain => { + ShapeExtent::ClosestSide | ShapeExtent::Contain => { let dist = get_distance_to_sides(size, center, ::std::cmp::min); ::std::cmp::min(dist.width, dist.height) } - FarthestSide => { + ShapeExtent::FarthestSide => { let dist = get_distance_to_sides(size, center, ::std::cmp::max); ::std::cmp::max(dist.width, dist.height) } - ClosestCorner => get_distance_to_corner(size, center, ::std::cmp::min), - FarthestCorner | Cover => get_distance_to_corner(size, center, ::std::cmp::max), + ShapeExtent::ClosestCorner => { + get_distance_to_corner(size, center, ::std::cmp::min) + }, + ShapeExtent::FarthestCorner | ShapeExtent::Cover => { + get_distance_to_corner(size, center, ::std::cmp::max) + }, }; Size2D::new(radius, radius) } /// Determines the radius of an ellipse if it was not explictly provided. /// https://drafts.csswg.org/css-images-3/#typedef-size -fn convert_ellipse_size_keyword(keyword: SizeKeyword, +fn convert_ellipse_size_keyword(keyword: ShapeExtent, size: &Size2D, center: &Point2D) -> Size2D { - use style::values::computed::image::SizeKeyword::*; match keyword { - ClosestSide | Contain => get_distance_to_sides(size, center, ::std::cmp::min), - FarthestSide => get_distance_to_sides(size, center, ::std::cmp::max), - ClosestCorner => get_ellipse_radius(size, center, ::std::cmp::min), - FarthestCorner | Cover => get_ellipse_radius(size, center, ::std::cmp::max), + ShapeExtent::ClosestSide | ShapeExtent::Contain => { + get_distance_to_sides(size, center, ::std::cmp::min) + }, + ShapeExtent::FarthestSide => { + get_distance_to_sides(size, center, ::std::cmp::max) + }, + ShapeExtent::ClosestCorner => { + get_ellipse_radius(size, center, ::std::cmp::min) + }, + ShapeExtent::FarthestCorner | ShapeExtent::Cover => { + get_ellipse_radius(size, center, ::std::cmp::max) + }, } } @@ -1098,13 +1109,13 @@ impl FragmentDisplayListBuilding for Fragment { fn convert_linear_gradient(&self, bounds: &Rect, stops: &[GradientItem], - angle_or_corner: &AngleOrCorner, + direction: &LineDirection, repeating: bool, style: &ServoComputedValues) -> display_list::Gradient { - let angle = match *angle_or_corner { - AngleOrCorner::Angle(angle) => angle.radians(), - AngleOrCorner::Corner(horizontal, vertical) => { + let angle = match *direction { + LineDirection::Angle(angle) => angle.radians(), + LineDirection::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. @@ -1171,16 +1182,18 @@ impl FragmentDisplayListBuilding for Fragment { let center = Point2D::new(specified(center.horizontal, bounds.size.width), specified(center.vertical, bounds.size.height)); let radius = match *shape { - EndingShape::Circle(LengthOrKeyword::Length(length)) - => Size2D::new(length, length), - EndingShape::Circle(LengthOrKeyword::Keyword(word)) - => convert_circle_size_keyword(word, &bounds.size, ¢er), - EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(horizontal, - vertical)) - => Size2D::new(specified(horizontal, bounds.size.width), - specified(vertical, bounds.size.height)), - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(word)) - => convert_ellipse_size_keyword(word, &bounds.size, ¢er), + GenericEndingShape::Circle(Circle::Radius(length)) => { + Size2D::new(length, length) + }, + GenericEndingShape::Circle(Circle::Extent(extent)) => { + convert_circle_size_keyword(extent, &bounds.size, ¢er) + }, + GenericEndingShape::Ellipse(Ellipse::Radii(x, y)) => { + Size2D::new(specified(x, bounds.size.width), specified(y, bounds.size.height)) + }, + GenericEndingShape::Ellipse(Ellipse::Extent(extent)) => { + convert_ellipse_size_keyword(extent, &bounds.size, ¢er) + }, }; let mut stops = convert_gradient_stops(stops, radius.width, style); diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 7dc333964e4..91de1643b95 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -192,9 +192,8 @@ impl nsStyleImage { use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE}; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE}; use gecko_bindings::structs::nsStyleCoord; - use values::computed::{AngleOrCorner, GradientKind, GradientShape, LengthOrKeyword}; - use values::computed::LengthOrPercentageOrKeyword; - use values::specified::SizeKeyword; + use values::computed::image::LineDirection; + use values::generics::image::{Circle, Ellipse, EndingShape, GradientKind, ShapeExtent}; use values::specified::position::{X, Y}; let stop_count = gradient.items.len(); @@ -204,7 +203,7 @@ impl nsStyleImage { } let gecko_gradient = match gradient.kind { - GradientKind::Linear(angle_or_corner) => { + GradientKind::Linear(direction) => { let gecko_gradient = unsafe { Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8, @@ -213,15 +212,15 @@ impl nsStyleImage { stop_count as u32) }; - match angle_or_corner { - AngleOrCorner::Angle(angle) => { + match direction { + LineDirection::Angle(angle) => { unsafe { (*gecko_gradient).mAngle.set(angle); (*gecko_gradient).mBgPosX.set_value(CoordDataValue::None); (*gecko_gradient).mBgPosY.set_value(CoordDataValue::None); } }, - AngleOrCorner::Corner(horiz, vert) => { + LineDirection::Corner(horiz, vert) => { let percent_x = match horiz { X::Left => 0.0, X::Right => 1.0, @@ -245,28 +244,28 @@ impl nsStyleImage { GradientKind::Radial(shape, position) => { let keyword_to_gecko_size = |keyword| { match keyword { - SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, - SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, - SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, - SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, - SizeKeyword::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, - SizeKeyword::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, + ShapeExtent::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, + ShapeExtent::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, + ShapeExtent::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, + ShapeExtent::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, + ShapeExtent::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, + ShapeExtent::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, } }; let (gecko_shape, gecko_size) = match shape { - GradientShape::Circle(ref length) => { - let size = match *length { - LengthOrKeyword::Keyword(keyword) => { - keyword_to_gecko_size(keyword) + EndingShape::Circle(ref circle) => { + let size = match *circle { + Circle::Extent(extent) => { + keyword_to_gecko_size(extent) }, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, }; (NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8) }, - GradientShape::Ellipse(ref length) => { - let size = match *length { - LengthOrPercentageOrKeyword::Keyword(keyword) => { - keyword_to_gecko_size(keyword) + EndingShape::Ellipse(ref ellipse) => { + let size = match *ellipse { + Ellipse::Extent(extent) => { + keyword_to_gecko_size(extent) }, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, }; @@ -291,22 +290,19 @@ impl nsStyleImage { // Setting radius values depending shape match shape { - GradientShape::Circle(length) => { - if let LengthOrKeyword::Length(len) = length { - unsafe { - (*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(len.0)); - (*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(len.0)); - } + EndingShape::Circle(Circle::Radius(length)) => { + unsafe { + (*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(length.0)); + (*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(length.0)); } }, - GradientShape::Ellipse(length) => { - if let LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) = length { - unsafe { - (*gecko_gradient).mRadiusX.set(first_len); - (*gecko_gradient).mRadiusY.set(second_len); - } + EndingShape::Ellipse(Ellipse::Radii(x, y)) => { + unsafe { + (*gecko_gradient).mRadiusX.set(x); + (*gecko_gradient).mRadiusY.set(y); } }, + _ => {}, } unsafe { (*gecko_gradient).mBgPosX.set(position.horizontal); diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index b31da88d338..2141880895a 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -11,23 +11,49 @@ use cssparser::Color as CSSColor; use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; -use values::generics::image::{CompatMode, ColorStop as GenericColorStop}; -use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; -use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect}; use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue}; use values::computed::position::Position; -use values::specified; +use values::generics::image::{CompatMode, ColorStop as GenericColorStop, EndingShape as GenericEndingShape}; +use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; +use values::generics::image::{Image as GenericImage, GradientKind as GenericGradientKind}; +use values::generics::image::{ImageRect as GenericImageRect, LineDirection as GenericLineDirection}; +use values::specified::image::LineDirection as SpecifiedLineDirection; use values::specified::position::{X, Y}; -pub use values::specified::SizeKeyword; - /// Computed values for an image according to CSS-IMAGES. /// https://drafts.csswg.org/css-images/#image-values -pub type Image = GenericImage; +pub type Image = GenericImage; /// Computed values for a CSS gradient. /// https://drafts.csswg.org/css-images/#gradients -pub type Gradient = GenericGradient; +pub type Gradient = GenericGradient< + LineDirection, + Length, + LengthOrPercentage, + Position, + CSSColor, +>; + +/// A computed gradient kind. +pub type GradientKind = GenericGradientKind< + LineDirection, + Length, + LengthOrPercentage, + Position, +>; + +/// A computed gradient line direction. +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LineDirection { + /// An angle. + Angle(Angle), + /// A corner. + Corner(X, Y), +} + +/// A computed radial gradient ending shape. +pub type EndingShape = GenericEndingShape; /// A computed gradient item. pub type GradientItem = GenericGradientItem; @@ -38,355 +64,65 @@ pub type ColorStop = GenericColorStop; /// Computed values for ImageRect. pub type ImageRect = GenericImageRect; -impl ToCss for Gradient { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.compat_mode == CompatMode::WebKit { - try!(dest.write_str("-webkit-")); - } - if self.repeating { - try!(dest.write_str("repeating-")); - } - match self.kind { - GradientKind::Linear(angle_or_corner) => { - try!(dest.write_str("linear-gradient(")); - try!(angle_or_corner.to_css(dest, self.compat_mode)); - }, - 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 item in &self.items { - try!(dest.write_str(", ")); - try!(item.to_css(dest)); - } - try!(dest.write_str(")")); - Ok(()) - } -} - -impl fmt::Debug for Gradient { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.kind { - GradientKind::Linear(angle_or_corner) => { - let _ = write!(f, "{:?}", angle_or_corner); - }, - GradientKind::Radial(ref shape, position) => { - let _ = write!(f, "{:?} at {:?}", shape, position); - }, - } - - for item in &self.items { - let _ = write!(f, ", {:?}", item); - } - Ok(()) - } -} - -/// 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 { +impl GenericLineDirection for LineDirection { + fn points_downwards(&self) -> bool { 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)) - }, + LineDirection::Angle(angle) => angle.radians() == PI, + LineDirection::Corner(..) => false, } } - #[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 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 { + fn to_css(&self, dest: &mut W, compat_mode: CompatMode) -> 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(X, Y) -} - -impl ToComputedValue for specified::AngleOrCorner { - type ComputedValue = AngleOrCorner; - - #[inline] - fn to_computed_value(&self, context: &Context) -> AngleOrCorner { - match *self { - specified::AngleOrCorner::None => { - AngleOrCorner::Angle(Angle::from_radians(PI)) - }, - specified::AngleOrCorner::Angle(angle) => { - AngleOrCorner::Angle(angle.to_computed_value(context)) - }, - specified::AngleOrCorner::Corner(horizontal, vertical) => { - match (horizontal, vertical) { - (None, Some(Y::Top)) => { - AngleOrCorner::Angle(Angle::from_radians(0.0)) - }, - (Some(X::Right), None) => { - AngleOrCorner::Angle(Angle::from_radians(PI * 0.5)) - }, - (None, Some(Y::Bottom)) => { - AngleOrCorner::Angle(Angle::from_radians(PI)) - }, - (Some(X::Left), None) => { - AngleOrCorner::Angle(Angle::from_radians(PI * 1.5)) - }, - (Some(horizontal), Some(vertical)) => { - AngleOrCorner::Corner(horizontal, vertical) - }, - (None, None) => { - unreachable!() - } + LineDirection::Angle(ref angle) => angle.to_css(dest), + LineDirection::Corner(x, y) => { + if compat_mode == CompatMode::Modern { + dest.write_str("to ")?; } - } - } - } - - #[inline] - fn from_computed_value(computed: &AngleOrCorner) -> Self { - match *computed { - AngleOrCorner::Angle(ref angle) => { - specified::AngleOrCorner::Angle(specified::Angle::from_computed_value(angle)) + x.to_css(dest)?; + dest.write_str(" ")?; + y.to_css(dest) }, - AngleOrCorner::Corner(horizontal, vertical) => { - specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical)) - } } } } -impl AngleOrCorner { - fn to_css(&self, dest: &mut W, mode: CompatMode) -> fmt::Result where W: fmt::Write { +impl ToComputedValue for SpecifiedLineDirection { + type ComputedValue = LineDirection; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { - AngleOrCorner::Angle(angle) => angle.to_css(dest), - AngleOrCorner::Corner(horizontal, vertical) => { - if mode == CompatMode::Modern { - try!(dest.write_str("to ")); - } - try!(horizontal.to_css(dest)); - try!(dest.write_str(" ")); - try!(vertical.to_css(dest)); - Ok(()) - } + SpecifiedLineDirection::Angle(ref angle) => { + LineDirection::Angle(angle.to_computed_value(context)) + }, + SpecifiedLineDirection::Horizontal(X::Left) => { + LineDirection::Angle(Angle::Degree(270.)) + }, + SpecifiedLineDirection::Horizontal(X::Right) => { + LineDirection::Angle(Angle::Degree(90.)) + }, + SpecifiedLineDirection::Vertical(Y::Top) => { + LineDirection::Angle(Angle::Degree(0.)) + }, + SpecifiedLineDirection::Vertical(Y::Bottom) => { + LineDirection::Angle(Angle::Degree(180.)) + }, + SpecifiedLineDirection::Corner(x, y) => { + LineDirection::Corner(x, y) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + LineDirection::Angle(ref angle) => { + SpecifiedLineDirection::Angle(ToComputedValue::from_computed_value(angle)) + }, + LineDirection::Corner(x, y) => { + SpecifiedLineDirection::Corner(x, y) + }, } } } diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 5addd13be6d..1bab68160be 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -12,8 +12,7 @@ use super::{Number, ToComputedValue, Context}; use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified}; use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength}; -pub use super::image::{EndingShape as GradientShape, Gradient, GradientKind, Image}; -pub use super::image::{LengthOrKeyword, LengthOrPercentageOrKeyword}; +pub use super::image::Image; pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone}; impl ToComputedValue for specified::NoCalcLength { diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index f1721e552cd..56aeb62ee25 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -22,8 +22,7 @@ use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as G pub use app_units::Au; pub use cssparser::Color as CSSColor; -pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientItem, LayerImage}; -pub use self::image::{GradientKind, Image, ImageRect, LengthOrKeyword, LengthOrPercentageOrKeyword}; +pub use self::image::{Gradient, GradientItem, LayerImage, LineDirection, Image, ImageRect}; pub use super::{Auto, Either, None_}; #[cfg(feature = "gecko")] pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index de644215acc..55bb8d20d43 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -19,13 +19,13 @@ use values::specified::url::SpecifiedUrl; /// [image]: https://drafts.csswg.org/css-images/#image-values #[derive(Clone, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum Image { +pub enum Image { /// A `` image. Url(SpecifiedUrl), /// A `` image. - Gradient(G), + Gradient(Gradient), /// A `-moz-image-rect` image - Rect(ImageRect), + Rect(ImageRect), /// A `-moz-element(# )` Element(Atom), } @@ -34,11 +34,11 @@ pub enum Image { /// https://drafts.csswg.org/css-images/#gradients #[derive(Clone, PartialEq, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct Gradient { +pub struct Gradient { /// Gradients can be linear or radial. - pub kind: K, + pub kind: GradientKind, /// The color stops and interpolation hints. - pub items: Vec>, + pub items: Vec>, /// True if this is a repeating gradient. pub repeating: bool, /// Compatibility mode. @@ -55,26 +55,76 @@ pub enum CompatMode { WebKit, } +/// A gradient kind. +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum GradientKind { + /// A linear gradient. + Linear(LineDirection), + /// A radial gradient. + Radial(EndingShape, Position), +} + +/// A radial gradient's ending shape. +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum EndingShape { + /// A circular gradient. + Circle(Circle), + /// An elliptic gradient. + Ellipse(Ellipse), +} + +/// A circle shape. +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Circle { + /// A circle radius. + Radius(Length), + /// A circle extent. + Extent(ShapeExtent), +} + +/// An ellipse shape. +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Ellipse { + /// An ellipse pair of radii. + Radii(LengthOrPercentage, LengthOrPercentage), + /// An ellipse extent. + Extent(ShapeExtent), +} + +/// https://drafts.csswg.org/css-images/#typedef-extent-keyword +define_css_keyword_enum!(ShapeExtent: + "closest-side" => ClosestSide, + "farthest-side" => FarthestSide, + "closest-corner" => ClosestCorner, + "farthest-corner" => FarthestCorner, + "contain" => Contain, + "cover" => Cover +); + /// A gradient item. /// https://drafts.csswg.org/css-images-4/#color-stop-syntax #[derive(Clone, Copy, PartialEq, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum GradientItem { +pub enum GradientItem { /// A color stop. - ColorStop(ColorStop), + ColorStop(ColorStop), /// An interpolation hint. - InterpolationHint(L), + InterpolationHint(LengthOrPercentage), } /// A color stop. /// https://drafts.csswg.org/css-images/#typedef-color-stop-list #[derive(Clone, Copy, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct ColorStop { +pub struct ColorStop { /// The color of this stop. - pub color: C, + pub color: Color, /// The position of this stop. - pub position: Option, + pub position: Option, } /// Values for `moz-image-rect`. @@ -83,16 +133,16 @@ pub struct ColorStop { #[derive(Clone, PartialEq, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] -pub struct ImageRect { +pub struct ImageRect { pub url: SpecifiedUrl, - pub top: C, - pub bottom: C, - pub right: C, - pub left: C, + pub top: NumberOrPercentage, + pub bottom: NumberOrPercentage, + pub right: NumberOrPercentage, + pub left: NumberOrPercentage, } -impl fmt::Debug for Image - where G: fmt::Debug, N: fmt::Debug, +impl fmt::Debug for Image + where G: fmt::Debug, R: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -108,8 +158,8 @@ impl fmt::Debug for Image } } -impl ToCss for Image - where G: ToCss, N: ToCss, +impl ToCss for Image + where G: ToCss, R: ToCss, { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { @@ -125,7 +175,7 @@ impl ToCss for Image } } -impl HasViewportPercentage for Image +impl HasViewportPercentage for Image where G: HasViewportPercentage { fn has_viewport_percentage(&self) -> bool { @@ -136,11 +186,11 @@ impl HasViewportPercentage for Image } } -impl ToComputedValue for Image - where G: ToComputedValue, N: ToComputedValue, +impl ToComputedValue for Image + where G: ToComputedValue, R: ToComputedValue, { type ComputedValue = Image<::ComputedValue, - ::ComputedValue>; + ::ComputedValue>; fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { @@ -177,8 +227,64 @@ impl ToComputedValue for Image } } -impl HasViewportPercentage for Gradient - where K: HasViewportPercentage, L: HasViewportPercentage, +impl ToCss for Gradient + where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.compat_mode == CompatMode::WebKit { + dest.write_str("-webkit-")?; + } + if self.repeating { + dest.write_str("repeating-")?; + } + dest.write_str(self.kind.label())?; + dest.write_str("-gradient(")?; + let mut skip_comma = match self.kind { + GradientKind::Linear(ref direction) if direction.points_downwards() => true, + GradientKind::Linear(ref direction) => { + direction.to_css(dest, self.compat_mode)?; + false + }, + GradientKind::Radial(ref shape, ref position) => { + let omit_shape = match *shape { + EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) | + EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => { + true + }, + _ => false, + }; + if self.compat_mode == CompatMode::Modern { + if !omit_shape { + shape.to_css(dest)?; + dest.write_str(" ")?; + } + dest.write_str("at ")?; + position.to_css(dest)?; + } else { + position.to_css(dest)?; + if !omit_shape { + dest.write_str(", ")?; + shape.to_css(dest)?; + } + } + false + }, + }; + for item in &self.items { + if !skip_comma { + dest.write_str(", ")?; + } + skip_comma = false; + item.to_css(dest)?; + } + dest.write_str(")") + } +} + +impl HasViewportPercentage for Gradient + where L: HasViewportPercentage, + LoP: HasViewportPercentage, + P: HasViewportPercentage, { fn has_viewport_percentage(&self) -> bool { self.kind.has_viewport_percentage() || @@ -186,12 +292,18 @@ impl HasViewportPercentage for Gradient } } -impl ToComputedValue for Gradient - where K: ToComputedValue, C: ToComputedValue, L: ToComputedValue, +impl ToComputedValue for Gradient + where D: ToComputedValue, + L: ToComputedValue, + LoP: ToComputedValue, + P: ToComputedValue, + C: ToComputedValue, { - type ComputedValue = Gradient<::ComputedValue, - ::ComputedValue, - ::ComputedValue>; + type ComputedValue = Gradient<::ComputedValue, + ::ComputedValue, + ::ComputedValue, +

::ComputedValue, + ::ComputedValue>; fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { Gradient { @@ -212,6 +324,168 @@ impl ToComputedValue for Gradient } } +impl GradientKind { + fn label(&self) -> &str { + match *self { + GradientKind::Linear(..) => "linear", + GradientKind::Radial(..) => "radial", + } + } +} + +impl HasViewportPercentage for GradientKind + where L: HasViewportPercentage, + LoP: HasViewportPercentage, + P: HasViewportPercentage +{ + fn has_viewport_percentage(&self) -> bool { + match *self { + GradientKind::Linear(_) => false, + GradientKind::Radial(ref shape, ref position) => { + shape.has_viewport_percentage() || position.has_viewport_percentage() + }, + } + } +} + +impl ToComputedValue for GradientKind + where D: ToComputedValue, + L: ToComputedValue, + LoP: ToComputedValue, + P: ToComputedValue, +{ + type ComputedValue = GradientKind<::ComputedValue, + ::ComputedValue, + ::ComputedValue, +

::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + GradientKind::Linear(ref direction) => { + GradientKind::Linear(direction.to_computed_value(context)) + }, + GradientKind::Radial(ref shape, ref position) => { + GradientKind::Radial(shape.to_computed_value(context), position.to_computed_value(context)) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + GradientKind::Linear(ref direction) => { + GradientKind::Linear(ToComputedValue::from_computed_value(direction)) + }, + GradientKind::Radial(ref shape, ref position) => { + GradientKind::Radial( + ToComputedValue::from_computed_value(shape), + ToComputedValue::from_computed_value(position), + ) + } + } + } +} + +/// The direction of a linear gradient. +pub trait LineDirection { + /// Whether this direction points towards, and thus can be omitted. + fn points_downwards(&self) -> bool; + + /// Serialises this direction according to the compatibility mode. + fn to_css(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result + where W: fmt::Write; +} + +impl ToCss for EndingShape + where L: ToCss, LoP: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + EndingShape::Circle(Circle::Extent(ShapeExtent::FarthestCorner)) | + EndingShape::Circle(Circle::Extent(ShapeExtent::Cover)) => { + dest.write_str("circle") + }, + EndingShape::Circle(Circle::Extent(keyword)) => { + dest.write_str("circle ")?; + keyword.to_css(dest) + }, + EndingShape::Circle(Circle::Radius(ref length)) => { + length.to_css(dest) + }, + EndingShape::Ellipse(Ellipse::Extent(keyword)) => { + keyword.to_css(dest) + }, + EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => { + x.to_css(dest)?; + dest.write_str(" ")?; + y.to_css(dest) + }, + } + } +} + +impl HasViewportPercentage for EndingShape + where L: HasViewportPercentage, LoP: HasViewportPercentage, +{ + fn has_viewport_percentage(&self) -> bool { + match *self { + EndingShape::Circle(Circle::Radius(ref length)) => { + length.has_viewport_percentage() + }, + EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => { + x.has_viewport_percentage() || y.has_viewport_percentage() + }, + _ => false, + } + } +} + +impl ToComputedValue for EndingShape + where L: ToComputedValue, LoP: ToComputedValue, +{ + type ComputedValue = EndingShape<::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + EndingShape::Circle(Circle::Radius(ref length)) => { + EndingShape::Circle(Circle::Radius(length.to_computed_value(context))) + }, + EndingShape::Circle(Circle::Extent(extent)) => { + EndingShape::Circle(Circle::Extent(extent)) + }, + EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => { + EndingShape::Ellipse(Ellipse::Radii( + x.to_computed_value(context), + y.to_computed_value(context), + )) + }, + EndingShape::Ellipse(Ellipse::Extent(extent)) => { + EndingShape::Ellipse(Ellipse::Extent(extent)) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + EndingShape::Circle(Circle::Radius(ref length)) => { + EndingShape::Circle(Circle::Radius(ToComputedValue::from_computed_value(length))) + }, + EndingShape::Circle(Circle::Extent(extent)) => { + EndingShape::Circle(Circle::Extent(extent)) + }, + EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => { + EndingShape::Ellipse(Ellipse::Radii( + ToComputedValue::from_computed_value(x), + ToComputedValue::from_computed_value(y), + )) + }, + EndingShape::Ellipse(Ellipse::Extent(extent)) => { + EndingShape::Ellipse(Ellipse::Extent(extent)) + }, + } + } +} + impl ToCss for GradientItem where C: ToCss, L: ToCss, { @@ -342,7 +616,7 @@ impl ToComputedValue for ImageRect fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { ImageRect { - url: self.url.clone(), + url: self.url.to_computed_value(context), top: self.top.to_computed_value(context), right: self.right.to_computed_value(context), bottom: self.bottom.to_computed_value(context), @@ -352,7 +626,7 @@ impl ToComputedValue for ImageRect fn from_computed_value(computed: &Self::ComputedValue) -> Self { ImageRect { - url: computed.url.clone(), + url: ToComputedValue::from_computed_value(&computed.url), top: ToComputedValue::from_computed_value(&computed.top), right: ToComputedValue::from_computed_value(&computed.right), bottom: ToComputedValue::from_computed_value(&computed.bottom), diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 01352fc187c..12a87c7913a 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -12,22 +12,56 @@ use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; #[cfg(feature = "servo")] use servo_url::ServoUrl; +use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; -use values::generics::image::{CompatMode, ColorStop as GenericColorStop}; -use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; +use values::generics::image::{Circle, CompatMode, Ellipse, ColorStop as GenericColorStop}; +use values::generics::image::{EndingShape as GenericEndingShape, Gradient as GenericGradient}; +use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind}; use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect}; -use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage}; +use values::generics::image::{LineDirection as GenericsLineDirection, ShapeExtent}; +use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage, Percentage}; use values::specified::position::{Position, X, Y}; use values::specified::url::SpecifiedUrl; /// Specified values for an image according to CSS-IMAGES. /// https://drafts.csswg.org/css-images/#image-values -pub type Image = GenericImage; +pub type Image = GenericImage; /// Specified values for a CSS gradient. /// https://drafts.csswg.org/css-images/#gradients -pub type Gradient = GenericGradient; +pub type Gradient = GenericGradient< + LineDirection, + Length, + LengthOrPercentage, + Position, + CSSColor, +>; + +/// A specified gradient kind. +pub type GradientKind = GenericGradientKind< + LineDirection, + Length, + LengthOrPercentage, + Position, +>; + +/// A specified gradient line direction. +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LineDirection { + /// An angular direction. + Angle(Angle), + /// A horizontal direction. + Horizontal(X), + /// A vertical direction. + Vertical(Y), + /// A direction towards a corner of a box. + Corner(X, Y), +} + +/// A specified ending shape. +pub type EndingShape = GenericEndingShape; /// A specified gradient item. pub type GradientItem = GenericGradientItem; @@ -77,103 +111,59 @@ impl Image { } } -impl ToCss for Gradient { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.compat_mode == CompatMode::WebKit { - try!(dest.write_str("-webkit-")); - } - if self.repeating { - try!(dest.write_str("repeating-")); - } - let mut skipcomma = false; - match self.kind { - GradientKind::Linear(angle_or_corner) => { - try!(dest.write_str("linear-gradient(")); - try!(angle_or_corner.to_css(dest, self.compat_mode)); - if angle_or_corner == AngleOrCorner::None { - skipcomma = true; - } - }, - GradientKind::Radial(ref shape, ref position) => { - try!(dest.write_str("radial-gradient(")); - if self.compat_mode == CompatMode::Modern { - try!(shape.to_css(dest)); - try!(dest.write_str(" at ")); - try!(position.to_css(dest)); - } else { - try!(position.to_css(dest)); - try!(dest.write_str(", ")); - try!(shape.to_css(dest)); - } - }, - } - for item in &self.items { - if !skipcomma { - try!(dest.write_str(", ")); - } else { - skipcomma = false; - } - try!(item.to_css(dest)); - } - dest.write_str(")") - } -} - impl Gradient { /// Parses a gradient from the given arguments. pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result { - fn parse(context: &ParserContext, input: &mut Parser, parse_kind: F) - -> Result<(GradientKind, Vec), ()> - where F: FnOnce(&ParserContext, &mut Parser) -> Result - { - input.parse_nested_block(|input| { - let kind = try!(parse_kind(context, input)); - let items = try!(Gradient::parse_items(context, input)); - Ok((kind, items)) - }) - }; - let mut repeating = false; - let mut compat_mode = CompatMode::Modern; - let (gradient_kind, items) = match_ignore_ascii_case! { &try!(input.expect_function()), + enum Shape { + Linear, + Radial, + } + + let (shape, repeating, compat_mode) = match_ignore_ascii_case! { &try!(input.expect_function()), "linear-gradient" => { - try!(parse(context, input, GradientKind::parse_modern_linear)) + (Shape::Linear, false, CompatMode::Modern) }, "-webkit-linear-gradient" => { - compat_mode = CompatMode::WebKit; - try!(parse(context, input, GradientKind::parse_webkit_linear)) + (Shape::Linear, false, CompatMode::WebKit) }, "repeating-linear-gradient" => { - repeating = true; - try!(parse(context, input, GradientKind::parse_modern_linear)) + (Shape::Linear, true, CompatMode::Modern) }, "-webkit-repeating-linear-gradient" => { - repeating = true; - compat_mode = CompatMode::WebKit; - try!(parse(context, input, GradientKind::parse_webkit_linear)) + (Shape::Linear, true, CompatMode::WebKit) }, "radial-gradient" => { - try!(parse(context, input, GradientKind::parse_modern_radial)) + (Shape::Radial, false, CompatMode::Modern) }, "-webkit-radial-gradient" => { - compat_mode = CompatMode::WebKit; - try!(parse(context, input, GradientKind::parse_webkit_radial)) + (Shape::Radial, false, CompatMode::WebKit) }, "repeating-radial-gradient" => { - repeating = true; - try!(parse(context, input, GradientKind::parse_modern_radial)) + (Shape::Radial, true, CompatMode::Modern) }, "-webkit-repeating-radial-gradient" => { - repeating = true; - compat_mode = CompatMode::WebKit; - try!(parse(context, input, GradientKind::parse_webkit_radial)) + (Shape::Radial, true, CompatMode::WebKit) }, _ => { return Err(()); } }; + let (kind, items) = input.parse_nested_block(|i| { + let shape = match shape { + Shape::Linear => GradientKind::parse_linear(context, i, compat_mode)?, + Shape::Radial => GradientKind::parse_radial(context, i, compat_mode)?, + }; + let items = Gradient::parse_items(context, i)?; + Ok((shape, items)) + })?; + + if items.len() < 2 { + return Err(()); + } + Ok(Gradient { items: items, repeating: repeating, - kind: gradient_kind, + kind: kind, compat_mode: compat_mode, }) } @@ -197,167 +187,205 @@ impl Gradient { } } -/// 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 { - /// A ``: - /// - /// https://drafts.csswg.org/css-images/#funcdef-linear-gradient - Linear(AngleOrCorner), +impl GradientKind { + fn parse_linear(context: &ParserContext, + input: &mut Parser, + compat_mode: CompatMode) + -> Result { + let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode)) { + input.expect_comma()?; + d + } else { + LineDirection::Vertical(Y::Bottom) + }; + Ok(GenericGradientKind::Linear(direction)) + } - /// A ``: - /// - /// https://drafts.csswg.org/css-images/#radial-gradients - Radial(EndingShape, Position), + fn parse_radial(context: &ParserContext, + input: &mut Parser, + compat_mode: CompatMode) + -> Result { + let (shape, position) = if compat_mode == CompatMode::Modern { + let shape = input.try(|i| EndingShape::parse(context, i, compat_mode)); + let position = input.try(|i| { + i.expect_ident_matching("at")?; + Position::parse(context, i) + }); + (shape, position) + } else { + let position = input.try(|i| Position::parse(context, i)); + let shape = input.try(|i| { + if position.is_ok() { + i.expect_comma()?; + } + EndingShape::parse(context, i, compat_mode) + }); + (shape, position) + }; + + if shape.is_ok() || position.is_ok() { + input.expect_comma()?; + } + + let shape = shape.unwrap_or({ + GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) + }); + let position = position.unwrap_or(Position::center()); + Ok(GenericGradientKind::Radial(shape, position)) + } } -impl GradientKind { - /// Parses a linear gradient kind from the given arguments. - fn parse_modern_linear(context: &ParserContext, input: &mut Parser) -> Result { - let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { - try!(input.expect_comma()); - AngleOrCorner::Angle(angle) - } else { - if input.try(|i| i.expect_ident_matching("to")).is_ok() { - let (horizontal, vertical) = - if let Ok(value) = input.try(X::parse) { - (Some(value), input.try(Y::parse).ok()) - } else { - let value = try!(Y::parse(input)); - (input.try(X::parse).ok(), Some(value)) - }; - try!(input.expect_comma()); - AngleOrCorner::Corner(horizontal, vertical) - } else { - AngleOrCorner::None - } - }; - Ok(GradientKind::Linear(direction)) +impl GenericsLineDirection for LineDirection { + fn points_downwards(&self) -> bool { + match *self { + LineDirection::Angle(ref angle) => angle.radians() == PI, + LineDirection::Vertical(Y::Bottom) => true, + _ => false, + } } - fn parse_webkit_linear(context: &ParserContext, input: &mut Parser) -> Result { - let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { - AngleOrCorner::Angle(angle) - } else { - if let Ok(value) = input.try(X::parse) { - AngleOrCorner::Corner(Some(value), input.try(Y::parse).ok()) - } else { - if let Ok(value) = input.try(Y::parse) { - AngleOrCorner::Corner(input.try(X::parse).ok(), Some(value)) - } else { - AngleOrCorner::None + fn to_css(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result + where W: fmt::Write + { + match *self { + LineDirection::Angle(angle) => { + angle.to_css(dest) + }, + LineDirection::Horizontal(x) => { + if compat_mode == CompatMode::Modern { + dest.write_str("to ")?; + } + x.to_css(dest) + }, + LineDirection::Vertical(y) => { + if compat_mode == CompatMode::Modern { + dest.write_str("to ")?; + } + y.to_css(dest) + }, + LineDirection::Corner(x, y) => { + if compat_mode == CompatMode::Modern { + dest.write_str("to ")?; + } + x.to_css(dest)?; + dest.write_str(" ")?; + y.to_css(dest) + } + } + } +} + +impl LineDirection { + fn parse(context: &ParserContext, + input: &mut Parser, + compat_mode: CompatMode) + -> Result { + if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { + return Ok(LineDirection::Angle(angle)); + } + input.try(|i| { + if compat_mode == CompatMode::Modern { + i.expect_ident_matching("to")?; + } + if let Ok(x) = i.try(X::parse) { + if let Ok(y) = i.try(Y::parse) { + return Ok(LineDirection::Corner(x, y)); + } + return Ok(LineDirection::Horizontal(x)); + } + let y = Y::parse(i)?; + if let Ok(x) = i.try(X::parse) { + return Ok(LineDirection::Corner(x, y)); + } + Ok(LineDirection::Vertical(y)) + }) + } +} + +impl EndingShape { + fn parse(context: &ParserContext, + input: &mut Parser, + compat_mode: CompatMode) + -> Result { + if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) { + if input.try(|i| i.expect_ident_matching("circle")).is_ok() { + return Ok(GenericEndingShape::Circle(Circle::Extent(extent))); + } + let _ = input.try(|i| i.expect_ident_matching("ellipse")); + return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(extent))); + } + if input.try(|i| i.expect_ident_matching("circle")).is_ok() { + if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) { + return Ok(GenericEndingShape::Circle(Circle::Extent(extent))); + } + if compat_mode == CompatMode::Modern { + if let Ok(length) = input.try(|i| Length::parse(context, i)) { + return Ok(GenericEndingShape::Circle(Circle::Radius(length))); } } - }; - if direction != AngleOrCorner::None { - try!(input.expect_comma()); + return Ok(GenericEndingShape::Circle(Circle::Extent(ShapeExtent::FarthestCorner))); } - Ok(GradientKind::Linear(direction)) - } - - /// Parses a modern radial gradient from the given arguments. - pub fn parse_modern_radial(context: &ParserContext, input: &mut Parser) -> Result { - let mut needs_comma = true; - - // Ending shape and position can be in various order. Checks all probabilities. - let (shape, position) = if let Ok(position) = input.try(|i| parse_position(context, i)) { - // Handle just - (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position) - } else if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) { - // Handle ? ? - let _ = input.try(|input| input.expect_ident_matching("ellipse")); - (EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second)), - input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } else if let Ok(length) = input.try(|i| Length::parse(context, i)) { - // Handle ? ? - let _ = input.try(|input| input.expect_ident_matching("circle")); - (EndingShape::Circle(LengthOrKeyword::Length(length)), - input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } else if let Ok(keyword) = input.try(SizeKeyword::parse_modern) { - // Handle ? ? - let shape = if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) + if input.try(|i| i.expect_ident_matching("ellipse")).is_ok() { + if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) { + return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(extent))); + } + if compat_mode == CompatMode::Modern { + let pair: Result<_, ()> = input.try(|i| { + let x = LengthOrPercentage::parse(context, i)?; + let y = LengthOrPercentage::parse(context, i)?; + Ok((x, y)) + }); + if let Ok((x, y)) = pair { + return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(x, y))); + } + } + return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner))); + } + if let Ok(length) = input.try(|i| Length::parse(context, i)) { + if let Ok(y) = input.try(|i| LengthOrPercentage::parse(context, i)) { + if compat_mode == CompatMode::Modern { + let _ = input.try(|i| i.expect_ident_matching("ellipse")); + } + return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(length.into(), y))); + } + if compat_mode == CompatMode::Modern { + let y = input.try(|i| { + i.expect_ident_matching("ellipse")?; + LengthOrPercentage::parse(context, i) + }); + if let Ok(y) = y { + return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(length.into(), y))); + } + let _ = input.try(|i| i.expect_ident_matching("circle")); + } + return Ok(GenericEndingShape::Circle(Circle::Radius(length))); + } + input.try(|i| { + let x = Percentage::parse(context, i)?; + let y = if let Ok(y) = i.try(|i| LengthOrPercentage::parse(context, i)) { + if compat_mode == CompatMode::Modern { + let _ = i.try(|i| i.expect_ident_matching("ellipse")); + } + y } else { - let _ = input.try(|input| input.expect_ident_matching("ellipse")); - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) + if compat_mode == CompatMode::Modern { + i.expect_ident_matching("ellipse")?; + } + LengthOrPercentage::parse(context, i)? }; - (shape, input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } else { - // Handle ? ? - if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() { - // Handle ? ? - let length = input.try(|i| LengthOrPercentageOrKeyword::parse(context, i, SizeKeyword::parse_modern)) - .unwrap_or(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)); - (EndingShape::Ellipse(length), - input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } else if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - // Handle ? ? - let length = input.try(|i| LengthOrKeyword::parse(context, i, SizeKeyword::parse_modern)) - .unwrap_or(LengthOrKeyword::Keyword(SizeKeyword::FarthestCorner)); - (EndingShape::Circle(length), input.try(|i| parse_position(context, i)) - .unwrap_or(Position::center())) - } else { - // If there is no shape keyword, it should set to default. - needs_comma = false; - (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), - input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } - }; - - if needs_comma { - try!(input.expect_comma()); - } - - Ok(GradientKind::Radial(shape, position)) + Ok(GenericEndingShape::Ellipse(Ellipse::Radii(x.into(), y))) + }) } +} - /// Parses a webkit radial gradient from the given arguments. - /// https://compat.spec.whatwg.org/#css-gradients-webkit-radial-gradient - pub fn parse_webkit_radial(context: &ParserContext, input: &mut Parser) -> Result { - let position = if let Ok(position) = input.try(|i| Position::parse(context, i)) { - try!(input.expect_comma()); - position - } else { - Position::center() - }; - - let mut needs_comma = true; - - // Ending shape and position can be in various order. Checks all probabilities. - let shape = if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) { - EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second)) - } else if let Ok(keyword) = input.try(SizeKeyword::parse) { - // Handle ? - if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) - } else { - let _ = input.try(|input| input.expect_ident_matching("ellipse")); - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) - } - } else { - // Handle ? - if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() { - // Handle ? - let keyword = input.try(SizeKeyword::parse).unwrap_or((SizeKeyword::Cover)); - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) - } else if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - // Handle ? - let keyword = input.try(SizeKeyword::parse).unwrap_or((SizeKeyword::Cover)); - EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) - } else { - // If there is no shape keyword, it should set to default. - needs_comma = false; - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::Cover)) - } - }; - - if needs_comma { - try!(input.expect_comma()); +impl ShapeExtent { + fn parse_with_compat_mode(input: &mut Parser, + compat_mode: CompatMode) + -> Result { + match try!(Self::parse(input)) { + ShapeExtent::Contain | ShapeExtent::Cover if compat_mode == CompatMode::Modern => Err(()), + keyword => Ok(keyword), } - - Ok(GradientKind::Radial(shape, position)) } } @@ -390,54 +418,6 @@ impl Parse for ImageRect { } } -fn parse_two_length(context: &ParserContext, input: &mut Parser) - -> Result<(LengthOrPercentage, LengthOrPercentage), ()> { - let first = try!(LengthOrPercentage::parse(context, input)); - let second = try!(LengthOrPercentage::parse(context, input)); - Ok((first, second)) -} - -fn parse_position(context: &ParserContext, input: &mut Parser) -> Result { - try!(input.expect_ident_matching("at")); - input.try(|i| Position::parse(context, i)) -} - -/// Specified values for an angle or a corner in a linear gradient. -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum AngleOrCorner { - Angle(Angle), - Corner(Option, Option), - None, -} - -impl AngleOrCorner { - fn to_css(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result where W: fmt::Write { - match *self { - AngleOrCorner::None => Ok(()), - AngleOrCorner::Angle(angle) => angle.to_css(dest), - AngleOrCorner::Corner(horizontal, vertical) => { - if compat_mode == CompatMode::Modern { - try!(dest.write_str("to ")); - } - let mut horizontal_present = false; - if let Some(horizontal) = horizontal { - try!(horizontal.to_css(dest)); - horizontal_present = true; - } - if let Some(vertical) = vertical { - if horizontal_present { - try!(dest.write_str(" ")); - } - try!(vertical.to_css(dest)); - } - Ok(()) - } - } - } -} - impl Parse for ColorStop { fn parse(context: &ParserContext, input: &mut Parser) -> Result { Ok(ColorStop { @@ -447,115 +427,6 @@ impl Parse for ColorStop { } } -/// 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))] -#[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(()) - } -} - -/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum LengthOrKeyword { - Length(Length), - Keyword(SizeKeyword), -} - -impl LengthOrKeyword { - fn parse(context: &ParserContext, input: &mut Parser, parse_size_keyword: F) -> Result - where F: Fn(&mut Parser) -> Result - { - if let Ok(keyword) = input.try(parse_size_keyword) { - Ok(LengthOrKeyword::Keyword(keyword)) - } else { - Ok(LengthOrKeyword::Length(try!(Length::parse(context, 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))] -#[allow(missing_docs)] -pub enum LengthOrPercentageOrKeyword { - LengthOrPercentage(LengthOrPercentage, LengthOrPercentage), - Keyword(SizeKeyword), -} - - -impl LengthOrPercentageOrKeyword { - fn parse(context: &ParserContext, input: &mut Parser, parse_size_keyword: F) -> Result - where F: Fn(&mut Parser) -> Result - { - if let Ok(keyword) = input.try(parse_size_keyword) { - Ok(LengthOrPercentageOrKeyword::Keyword(keyword)) - } else { - Ok(LengthOrPercentageOrKeyword::LengthOrPercentage( - try!(LengthOrPercentage::parse(context, input)), - try!(LengthOrPercentage::parse(context, 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, ref 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, - "contain" => Contain, "cover" => Cover); - -impl SizeKeyword { - fn parse_modern(input: &mut Parser) -> Result { - match try!(SizeKeyword::parse(input)) { - SizeKeyword::Contain | SizeKeyword::Cover => Err(()), - keyword => Ok(keyword), - } - } -} - /// Specified values for none | | . #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index 13eb17b6a6f..27e5494b9f5 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -24,8 +24,8 @@ use values::computed::{ComputedValueAsSpecified, Context}; use values::specified::calc::CalcNode; pub use values::specified::calc::CalcLengthOrPercentage; -pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; -pub use super::image::{GradientKind, Image, LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword}; +pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; +pub use super::image::{GradientKind, Image}; /// Number of app units per pixel pub const AU_PER_PX: CSSFloat = 60.; diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index d7245b25d2a..c0e9bfbd0fb 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -28,9 +28,8 @@ use values::specified::calc::CalcNode; pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use self::color::Color; pub use self::grid::{GridLine, TrackKeyword}; -pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; +pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; pub use self::image::{GradientItem, GradientKind, Image, ImageRect, LayerImage}; -pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword}; pub use self::length::AbsoluteLength; pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage}; pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; diff --git a/tests/unit/style/parsing/image.rs b/tests/unit/style/parsing/image.rs index 708c50b0d93..b4956e8edb7 100644 --- a/tests/unit/style/parsing/image.rs +++ b/tests/unit/style/parsing/image.rs @@ -2,16 +2,7 @@ * 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/. */ -use euclid::size::TypedSize2D; use parsing::parse; -use std::f32::consts::PI; -use style::context::QuirksMode; -use style::font_metrics::ServoMetricsProvider; -use style::media_queries::{Device, MediaType}; -use style::properties::{ComputedValues, StyleBuilder}; -use style::values::computed; -use style::values::computed::{Angle, Context, ToComputedValue}; -use style::values::specified; use style::values::specified::image::*; use style_traits::ToCss; @@ -37,101 +28,77 @@ fn test_linear_gradient() { // Parsing without and assert_roundtrip_with_context!(Image::parse, "linear-gradient(red, green)"); - - // AngleOrCorner::None should become AngleOrCorner::Angle(Angle(PI)) when parsed - // Note that Angle(PI) is correct for top-to-bottom rendering, whereas Angle(0) would render bottom-to-top. - // ref: https://developer.mozilla.org/en-US/docs/Web/CSS/angle - let viewport_size = TypedSize2D::new(0., 0.); - let initial_style = ComputedValues::initial_values(); - let device = Device::new(MediaType::Screen, viewport_size); - let specified_context = Context { - is_root_element: true, - device: &device, - inherited_style: initial_style, - layout_parent_style: initial_style, - style: StyleBuilder::for_derived_style(&initial_style), - cached_system_font: None, - font_metrics_provider: &ServoMetricsProvider, - in_media_query: false, - quirks_mode: QuirksMode::NoQuirks, - }; - assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context), - computed::AngleOrCorner::Angle(Angle::from_radians(PI))); } #[test] fn test_radial_gradient() { // Parsing with all values assert_roundtrip_with_context!(Image::parse, "radial-gradient(circle closest-side at 20px 30px, red, green)"); - assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse closest-side at 20px 30px, red, green)"); + assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse closest-side at 20px 30px, red, green)", + "radial-gradient(closest-side at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)", "radial-gradient(circle closest-side at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side ellipse at 20px 30px, red, green)", - "radial-gradient(ellipse closest-side at 20px 30px, red, green)"); + "radial-gradient(closest-side at 20px 30px, red, green)"); // Parsing with and reversed assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)", "radial-gradient(circle closest-side at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-corner ellipse at 20px 30px, red, green)", - "radial-gradient(ellipse closest-corner at 20px 30px, red, green)"); + "radial-gradient(closest-corner at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(30px circle, red, green)", - "radial-gradient(circle 30px at center center, red, green)"); + "radial-gradient(30px at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(30px 40px ellipse, red, green)", - "radial-gradient(ellipse 30px 40px at center center, red, green)"); + "radial-gradient(30px 40px at center center, red, green)"); // Parsing without assert_roundtrip_with_context!(Image::parse, "radial-gradient(circle, red, green)", - "radial-gradient(circle farthest-corner at center center, red, green)"); + "radial-gradient(circle at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse, red, green)", - "radial-gradient(ellipse farthest-corner at center center, red, green)"); + "radial-gradient(at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, - "radial-gradient(circle at 20px 30px, red, green)", - "radial-gradient(circle farthest-corner at 20px 30px, red, green)"); + "radial-gradient(circle at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse at 20px 30px, red, green)", - "radial-gradient(ellipse farthest-corner at 20px 30px, red, green)"); + "radial-gradient(at 20px 30px, red, green)"); // Parsing without assert_roundtrip_with_context!(Image::parse, - "radial-gradient(20px at 20px 30px, red, green)", - "radial-gradient(circle 20px at 20px 30px, red, green)"); + "radial-gradient(20px at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, - "radial-gradient(20px 30px at left center, red, green)", - "radial-gradient(ellipse 20px 30px at left center, red, green)"); + "radial-gradient(20px 30px at left center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side at center, red, green)", - "radial-gradient(ellipse closest-side at center center, red, green)"); + "radial-gradient(closest-side at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(20px, red, green)", - "radial-gradient(circle 20px at center center, red, green)"); + "radial-gradient(20px at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(20px 30px, red, green)", - "radial-gradient(ellipse 20px 30px at center center, red, green)"); + "radial-gradient(20px 30px at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side, red, green)", - "radial-gradient(ellipse closest-side at center center, red, green)"); + "radial-gradient(closest-side at center center, red, green)"); // Parsing without and assert_roundtrip_with_context!(Image::parse, "radial-gradient(at center, red, green)", - "radial-gradient(ellipse farthest-corner at center center, red, green)"); + "radial-gradient(at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, - "radial-gradient(at center bottom, red, green)", - "radial-gradient(ellipse farthest-corner at center bottom, red, green)"); + "radial-gradient(at center bottom, red, green)"); assert_roundtrip_with_context!(Image::parse, - "radial-gradient(at 40px 50px, red, green)", - "radial-gradient(ellipse farthest-corner at 40px 50px, red, green)"); + "radial-gradient(at 40px 50px, red, green)"); // Parsing with just color stops assert_roundtrip_with_context!(Image::parse, "radial-gradient(red, green)", - "radial-gradient(ellipse farthest-corner at center center, red, green)"); + "radial-gradient(at center center, red, green)"); // Parsing repeating radial gradient assert_roundtrip_with_context!(Image::parse, "repeating-radial-gradient(red, green)", - "repeating-radial-gradient(ellipse farthest-corner at center center, red, green)"); + "repeating-radial-gradient(at center center, red, green)"); } From fa5b46b6eecf1c75a71b43c1d996e2d363e2a719 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 12 May 2017 22:25:23 +0200 Subject: [PATCH 4/9] Clean up methods on specified::Image --- components/style/values/specified/image.rs | 24 ++++++++++------------ tests/unit/style/parsing/image.rs | 1 + 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 12a87c7913a..692a01ffdae 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -73,9 +73,8 @@ pub type ColorStop = GenericColorStop; /// -moz-image-rect(, top, right, bottom, left); pub type ImageRect = GenericImageRect; -impl Image { - #[allow(missing_docs)] - pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { +impl Parse for Image { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { if let Ok(url) = input.try(|input| SpecifiedUrl::parse(context, input)) { return Ok(GenericImage::Url(url)); } @@ -88,7 +87,9 @@ impl Image { Ok(GenericImage::Element(Image::parse_element(input)?)) } +} +impl Image { /// Creates an already specified image value from an already resolved URL /// for insertion in the cascade. #[cfg(feature = "servo")] @@ -98,16 +99,13 @@ impl Image { /// Parses a `-moz-element(# )`. fn parse_element(input: &mut Parser) -> Result { - if input.try(|i| i.expect_function_matching("-moz-element")).is_ok() { - input.parse_nested_block(|i| { - match i.next()? { - Token::IDHash(id) => Ok(Atom::from(id)), - _ => Err(()), - } - }) - } else { - Err(()) - } + input.try(|i| i.expect_function_matching("-moz-element"))?; + input.parse_nested_block(|i| { + match i.next()? { + Token::IDHash(id) => Ok(Atom::from(id)), + _ => Err(()), + } + }) } } diff --git a/tests/unit/style/parsing/image.rs b/tests/unit/style/parsing/image.rs index b4956e8edb7..481fbbb952d 100644 --- a/tests/unit/style/parsing/image.rs +++ b/tests/unit/style/parsing/image.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use parsing::parse; +use style::parser::Parse; use style::values::specified::image::*; use style_traits::ToCss; From 73f77e0a5369ede557afa1f4347c9a146bc5dc39 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 12 May 2017 22:24:30 +0200 Subject: [PATCH 5/9] Rename LayerImage to ImageLayer and make it a type alias --- components/layout/display_list_builder.rs | 24 ++++----- components/script/dom/element.rs | 6 +-- components/style/properties/gecko.mako.rs | 4 +- .../properties/longhand/background.mako.rs | 6 +-- .../style/properties/longhand/border.mako.rs | 6 +-- .../style/properties/longhand/svg.mako.rs | 7 +-- components/style/values/computed/image.rs | 18 ++----- components/style/values/computed/mod.rs | 2 +- components/style/values/specified/image.rs | 52 ++----------------- components/style/values/specified/mod.rs | 2 +- ports/geckolib/glue.rs | 6 +-- tests/unit/style/properties/serialization.rs | 15 +++--- 12 files changed, 47 insertions(+), 101 deletions(-) diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 5f33702d693..c3ac7f09048 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -865,9 +865,9 @@ impl FragmentDisplayListBuilding for Fragment { // http://www.w3.org/TR/CSS21/colors.html#background let background = style.get_background(); for (i, background_image) in background.background_image.0.iter().enumerate().rev() { - match background_image.0 { - None => {} - Some(Image::Gradient(ref gradient)) => { + match *background_image { + Either::First(_) => {} + Either::Second(Image::Gradient(ref gradient)) => { self.build_display_list_for_background_gradient(state, display_list_section, &absolute_bounds, @@ -876,7 +876,7 @@ impl FragmentDisplayListBuilding for Fragment { gradient, style); } - Some(Image::Url(ref image_url)) => { + Either::Second(Image::Url(ref image_url)) => { if let Some(url) = image_url.url() { self.build_display_list_for_background_image(state, style, @@ -887,10 +887,10 @@ impl FragmentDisplayListBuilding for Fragment { i); } } - Some(Image::Rect(_)) => { + Either::Second(Image::Rect(_)) => { // TODO: Implement `-moz-image-rect` } - Some(Image::Element(_)) => { + Either::Second(Image::Element(_)) => { // TODO: Implement `-moz-element` } } @@ -1356,8 +1356,8 @@ impl FragmentDisplayListBuilding for Fragment { style.get_cursor(Cursor::Default), display_list_section); - match border_style_struct.border_image_source.0 { - None => { + match border_style_struct.border_image_source { + Either::First(_) => { state.add_display_item(DisplayItem::Border(box BorderDisplayItem { base: base, border_widths: border.to_physical(style.writing_mode), @@ -1371,7 +1371,7 @@ impl FragmentDisplayListBuilding for Fragment { }), })); } - Some(Image::Gradient(ref gradient)) => { + Either::Second(Image::Gradient(ref gradient)) => { match gradient.kind { GradientKind::Linear(angle_or_corner) => { let grad = self.convert_linear_gradient(&bounds, @@ -1412,13 +1412,13 @@ impl FragmentDisplayListBuilding for Fragment { } } } - Some(Image::Rect(..)) => { + Either::Second(Image::Rect(..)) => { // TODO: Handle border-image with `-moz-image-rect`. } - Some(Image::Element(..)) => { + Either::Second(Image::Element(..)) => { // TODO: Handle border-image with `-moz-element`. } - Some(Image::Url(ref image_url)) => { + Either::Second(Image::Url(ref image_url)) => { if let Some(url) = image_url.url() { let webrender_image = state.layout_context .get_webrender_image_for_url(self.node, diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index d54b79cdc28..aa32370127c 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -109,7 +109,7 @@ use style::sink::Push; use style::stylearc::Arc; use style::stylist::ApplicableDeclarationBlock; use style::thread_state; -use style::values::CSSFloat; +use style::values::{CSSFloat, Either}; use style::values::specified::{self, CSSColor}; use stylesheet_loader::StylesheetOwner; @@ -427,9 +427,7 @@ impl LayoutElementHelpers for LayoutJS { shared_lock, PropertyDeclaration::BackgroundImage( background_image::SpecifiedValue(vec![ - background_image::single_value::SpecifiedValue(Some( - specified::Image::for_cascade(url.into()) - )) + Either::Second(specified::Image::for_cascade(url.into())) ])))); } diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 4271b424bf1..f759bc680ce 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -927,7 +927,7 @@ fn static_assert() { Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource); } - if let Some(image) = image.0 { + if let Either::Second(image) = image { self.gecko.mBorderImageSource.set(image, &mut false) } } @@ -2883,7 +2883,7 @@ fn static_assert() { for (image, geckoimage) in images.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { - if let Some(image) = image.0 { + if let Either::Second(image) = image { geckoimage.mImage.set(image, cacheable) } } diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index b0c998d59fd..563fdecfc5a 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -12,9 +12,9 @@ ${helpers.predefined_type("background-color", "CSSColor", spec="https://drafts.csswg.org/css-backgrounds/#background-color", animation_value_type="IntermediateColor", complex_color=True)} -${helpers.predefined_type("background-image", "LayerImage", - initial_value="computed_value::T(None)", - initial_specified_value="SpecifiedValue(None)", +${helpers.predefined_type("background-image", "ImageLayer", + initial_value="Either::First(None_)", + initial_specified_value="Either::First(None_)", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector="True", animation_value_type="none", diff --git a/components/style/properties/longhand/border.mako.rs b/components/style/properties/longhand/border.mako.rs index 6f07db69038..86cf3bd0e7f 100644 --- a/components/style/properties/longhand/border.mako.rs +++ b/components/style/properties/longhand/border.mako.rs @@ -190,9 +190,9 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)", animation_value_type="none")} -${helpers.predefined_type("border-image-source", "LayerImage", - initial_value="computed_value::T(None)", - initial_specified_value="SpecifiedValue(None)", +${helpers.predefined_type("border-image-source", "ImageLayer", + initial_value="Either::First(None_)", + initial_specified_value="Either::First(None_)", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector=False, animation_value_type="none", diff --git a/components/style/properties/longhand/svg.mako.rs b/components/style/properties/longhand/svg.mako.rs index c1327a6ca2e..540c522c09d 100644 --- a/components/style/properties/longhand/svg.mako.rs +++ b/components/style/properties/longhand/svg.mako.rs @@ -141,9 +141,10 @@ ${helpers.single_keyword("mask-composite", extra_prefixes="webkit", animation_value_type="none", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite")} -${helpers.predefined_type("mask-image", "LayerImage", - initial_value="computed_value::T(None)", - initial_specified_value="SpecifiedValue(None)", + +${helpers.predefined_type("mask-image", "ImageLayer", + initial_value="Either::First(None_)", + initial_specified_value="Either::First(None_)", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image", vector=True, products="gecko", diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 2141880895a..5f51dd22f85 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -11,6 +11,7 @@ use cssparser::Color as CSSColor; use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; +use values::{Either, None_}; use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue}; use values::computed::position::Position; use values::generics::image::{CompatMode, ColorStop as GenericColorStop, EndingShape as GenericEndingShape}; @@ -20,6 +21,9 @@ use values::generics::image::{ImageRect as GenericImageRect, LineDirection as Ge use values::specified::image::LineDirection as SpecifiedLineDirection; use values::specified::position::{X, Y}; +/// A computed image layer. +pub type ImageLayer = Either; + /// Computed values for an image according to CSS-IMAGES. /// https://drafts.csswg.org/css-images/#image-values pub type Image = GenericImage; @@ -126,17 +130,3 @@ impl ToComputedValue for SpecifiedLineDirection { } } } - -/// Computed values for none | | . -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct LayerImage(pub Option); - -impl ToCss for LayerImage { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("none"), - Some(ref image) => image.to_css(dest), - } - } -} diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 56aeb62ee25..02a71c6b313 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -22,7 +22,7 @@ use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as G pub use app_units::Au; pub use cssparser::Color as CSSColor; -pub use self::image::{Gradient, GradientItem, LayerImage, LineDirection, Image, ImageRect}; +pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect}; pub use super::{Auto, Either, None_}; #[cfg(feature = "gecko")] pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 692a01ffdae..7ec97ad4db3 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -15,6 +15,7 @@ use servo_url::ServoUrl; use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; +use values::{Either, None_}; use values::generics::image::{Circle, CompatMode, Ellipse, ColorStop as GenericColorStop}; use values::generics::image::{EndingShape as GenericEndingShape, Gradient as GenericGradient}; use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind}; @@ -24,6 +25,9 @@ use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPer use values::specified::position::{Position, X, Y}; use values::specified::url::SpecifiedUrl; +/// A specified image layer. +pub type ImageLayer = Either; + /// Specified values for an image according to CSS-IMAGES. /// https://drafts.csswg.org/css-images/#image-values pub type Image = GenericImage; @@ -424,51 +428,3 @@ impl Parse for ColorStop { }) } } - -/// Specified values for none | | . -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct LayerImage(pub Option); -no_viewport_percentage!(LayerImage); - -impl ToCss for LayerImage { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LayerImage(Some(ref image)) => image.to_css(dest), - LayerImage(None) => dest.write_str("none"), - } - } -} - -use super::computed::{ToComputedValue, Context}; -impl ToComputedValue for LayerImage { - type ComputedValue = super::computed::LayerImage; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - LayerImage(None) => super::computed::LayerImage(None), - LayerImage(Some(ref image)) => - super::computed::LayerImage(Some(image.to_computed_value(context))), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - super::computed::LayerImage(None) => LayerImage(None), - super::computed::LayerImage(Some(ref image)) => - LayerImage(Some(ToComputedValue::from_computed_value(image))), - } - } -} - -impl Parse for LayerImage { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if input.try(|input| input.expect_ident_matching("none")).is_ok() { - Ok(LayerImage(None)) - } else { - Ok(LayerImage(Some(try!(Image::parse(context, input))))) - } - } -} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index c0e9bfbd0fb..70e0ea75e8d 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -29,7 +29,7 @@ pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, Justify pub use self::color::Color; pub use self::grid::{GridLine, TrackKeyword}; pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; -pub use self::image::{GradientItem, GradientKind, Image, ImageRect, LayerImage}; +pub use self::image::{GradientItem, GradientKind, Image, ImageRect, ImageLayer}; pub use self::length::AbsoluteLength; pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage}; pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 822f34056e4..966d6785049 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -1800,7 +1800,7 @@ pub extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(declarations: raw_extra_data: *mut URLExtraData) { use style::properties::PropertyDeclaration; use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage; - use style::properties::longhands::background_image::single_value::SpecifiedValue as SingleBackgroundImage; + use style::values::Either; use style::values::generics::image::Image; use style::values::specified::url::SpecifiedUrl; @@ -1812,9 +1812,7 @@ pub extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(declarations: QuirksMode::NoQuirks); if let Ok(url) = SpecifiedUrl::parse_from_string(string.into(), &context) { let decl = PropertyDeclaration::BackgroundImage(BackgroundImage( - vec![SingleBackgroundImage( - Some(Image::Url(url)) - )] + vec![Either::Second(Image::Url(url))] )); write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| { decls.push(decl, Importance::Normal); diff --git a/tests/unit/style/properties/serialization.rs b/tests/unit/style/properties/serialization.rs index d784454f712..385aa900214 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -799,6 +799,7 @@ mod shorthand_serialization { use style::properties::longhands::mask_position_y as position_y; use style::properties::longhands::mask_repeat as repeat; use style::properties::longhands::mask_size as size; + use style::values::Either; use style::values::generics::image::Image; use super::*; @@ -828,9 +829,10 @@ mod shorthand_serialization { fn mask_should_serialize_all_available_properties_when_specified() { let mut properties = Vec::new(); - let image = single_vec_value_typedef!(image, - image::single_value::SpecifiedValue( - Some(Image::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"))))); + let image = single_vec_value_typedef!( + image, + Either::Second(Image::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"))) + ); let mode = single_vec_keyword_value!(mode, luminance); @@ -880,9 +882,10 @@ mod shorthand_serialization { fn mask_should_combine_origin_and_clip_properties_when_equal() { let mut properties = Vec::new(); - let image = single_vec_value_typedef!(image, - image::single_value::SpecifiedValue( - Some(Image::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"))))); + let image = single_vec_value_typedef!( + image, + Either::Second(Image::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"))) + ); let mode = single_vec_keyword_value!(mode, luminance); From c9d140121d70cedd239ae5df3b9110074345902a Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 13 May 2017 10:43:04 +0200 Subject: [PATCH 6/9] Accept strings in -moz-image-rect() (fixes #16823) --- components/style/values/specified/image.rs | 55 ++++++++++------------ 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 7ec97ad4db3..166eed64fe3 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -391,35 +391,6 @@ impl ShapeExtent { } } -impl Parse for ImageRect { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - match_ignore_ascii_case! { &try!(input.expect_function()), - "-moz-image-rect" => { - input.parse_nested_block(|input| { - let url = SpecifiedUrl::parse(context, input)?; - input.expect_comma()?; - let top = NumberOrPercentage::parse(context, input)?; - input.expect_comma()?; - let right = NumberOrPercentage::parse(context, input)?; - input.expect_comma()?; - let bottom = NumberOrPercentage::parse(context, input)?; - input.expect_comma()?; - let left = NumberOrPercentage::parse(context, input)?; - - Ok(ImageRect { - url: url, - top: top, - right: right, - bottom: bottom, - left: left, - }) - }) - } - _ => Err(()) - } - } -} - impl Parse for ColorStop { fn parse(context: &ParserContext, input: &mut Parser) -> Result { Ok(ColorStop { @@ -428,3 +399,29 @@ impl Parse for ColorStop { }) } } + +impl Parse for ImageRect { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + input.try(|i| i.expect_function_matching("-moz-image-rect"))?; + input.parse_nested_block(|i| { + let string = i.expect_url_or_string()?; + let url = SpecifiedUrl::parse_from_string(string, context)?; + i.expect_comma()?; + let top = NumberOrPercentage::parse(context, i)?; + i.expect_comma()?; + let right = NumberOrPercentage::parse(context, i)?; + i.expect_comma()?; + let bottom = NumberOrPercentage::parse(context, i)?; + i.expect_comma()?; + let left = NumberOrPercentage::parse(context, i)?; + + Ok(ImageRect { + url: url, + top: top, + right: right, + bottom: bottom, + left: left, + }) + }) + } +} From 9e6f9db1275c7bfaeb3584be330e5e09ac4c3eab Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 13 May 2017 10:57:46 +0200 Subject: [PATCH 7/9] Reorganise specified::image::GradientItem methods --- components/style/values/specified/image.rs | 47 +++++++++++----------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 166eed64fe3..f85535d011d 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -82,7 +82,7 @@ impl Parse for Image { if let Ok(url) = input.try(|input| SpecifiedUrl::parse(context, input)) { return Ok(GenericImage::Url(url)); } - if let Ok(gradient) = input.try(|input| Gradient::parse_function(context, input)) { + if let Ok(gradient) = input.try(|i| Gradient::parse(context, i)) { return Ok(GenericImage::Gradient(gradient)); } if let Ok(image_rect) = input.try(|input| ImageRect::parse(context, input)) { @@ -113,9 +113,8 @@ impl Image { } } -impl Gradient { - /// Parses a gradient from the given arguments. - pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result { +impl Parse for Gradient { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { enum Shape { Linear, Radial, @@ -154,7 +153,7 @@ impl Gradient { Shape::Linear => GradientKind::parse_linear(context, i, compat_mode)?, Shape::Radial => GradientKind::parse_radial(context, i, compat_mode)?, }; - let items = Gradient::parse_items(context, i)?; + let items = GradientItem::parse_comma_separated(context, i)?; Ok((shape, items)) })?; @@ -169,24 +168,6 @@ impl Gradient { compat_mode: compat_mode, }) } - - fn parse_items(context: &ParserContext, input: &mut Parser) -> Result, ()> { - let mut seen_stop = false; - let items = try!(input.parse_comma_separated(|input| { - if seen_stop { - if let Ok(hint) = input.try(|i| LengthOrPercentage::parse(context, i)) { - seen_stop = false; - return Ok(GenericGradientItem::InterpolationHint(hint)); - } - } - seen_stop = true; - ColorStop::parse(context, input).map(GenericGradientItem::ColorStop) - })); - if !seen_stop || items.len() < 2 { - return Err(()); - } - Ok(items) - } } impl GradientKind { @@ -391,6 +372,26 @@ impl ShapeExtent { } } +impl GradientItem { + fn parse_comma_separated(context: &ParserContext, input: &mut Parser) -> Result, ()> { + let mut seen_stop = false; + let items = try!(input.parse_comma_separated(|input| { + if seen_stop { + if let Ok(hint) = input.try(|i| LengthOrPercentage::parse(context, i)) { + seen_stop = false; + return Ok(GenericGradientItem::InterpolationHint(hint)); + } + } + seen_stop = true; + ColorStop::parse(context, input).map(GenericGradientItem::ColorStop) + })); + if !seen_stop || items.len() < 2 { + return Err(()); + } + Ok(items) + } +} + impl Parse for ColorStop { fn parse(context: &ParserContext, input: &mut Parser) -> Result { Ok(ColorStop { From ea4e7299d40e788da18383767dc445b2482df824 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 13 May 2017 16:46:58 +0200 Subject: [PATCH 8/9] Implement -webkit-gradient() (fixes #16542) --- .../style/properties/longhand/border.mako.rs | 2 +- components/style/values/specified/image.rs | 264 +++++++++++++++++- components/style/values/specified/length.rs | 11 +- components/style/values/specified/mod.rs | 24 +- 4 files changed, 285 insertions(+), 16 deletions(-) diff --git a/components/style/properties/longhand/border.mako.rs b/components/style/properties/longhand/border.mako.rs index 86cf3bd0e7f..f18ae154428 100644 --- a/components/style/properties/longhand/border.mako.rs +++ b/components/style/properties/longhand/border.mako.rs @@ -717,7 +717,7 @@ ${helpers.predefined_type("border-image-source", "ImageLayer", let mut values = vec![]; for _ in 0..4 { - let value = input.try(|input| NumberOrPercentage::parse(context, input)); + let value = input.try(|input| NumberOrPercentage::parse_non_negative(context, input)); match value { Ok(val) => values.push(val), Err(_) => break, diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index f85535d011d..dcbc5db1c5c 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -12,6 +12,7 @@ use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; #[cfg(feature = "servo")] use servo_url::ServoUrl; +use std::cmp::Ordering; use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; @@ -21,8 +22,10 @@ use values::generics::image::{EndingShape as GenericEndingShape, Gradient as Gen use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind}; use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect}; use values::generics::image::{LineDirection as GenericsLineDirection, ShapeExtent}; -use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage, Percentage}; -use values::specified::position::{Position, X, Y}; +use values::generics::position::Position as GenericPosition; +use values::specified::{Angle, CSSColor, Color, Length, LengthOrPercentage}; +use values::specified::{Number, NumberOrPercentage, Percentage}; +use values::specified::position::{Position, PositionComponent, Side, X, Y}; use values::specified::url::SpecifiedUrl; /// A specified image layer. @@ -145,6 +148,9 @@ impl Parse for Gradient { "-webkit-repeating-radial-gradient" => { (Shape::Radial, true, CompatMode::WebKit) }, + "-webkit-gradient" => { + return input.parse_nested_block(|i| Self::parse_webkit_gradient_argument(context, i)); + }, _ => { return Err(()); } }; @@ -170,6 +176,252 @@ impl Parse for Gradient { } } +impl Gradient { + fn parse_webkit_gradient_argument(context: &ParserContext, input: &mut Parser) -> Result { + type Point = GenericPosition, Component>; + + #[derive(Clone, Copy)] + enum Component { + Center, + Number(NumberOrPercentage), + Side(S), + } + + impl LineDirection { + fn from_points(first: Point, second: Point) -> Self { + let h_ord = first.horizontal.partial_cmp(&second.horizontal); + let v_ord = first.vertical.partial_cmp(&second.vertical); + let (h, v) = match (h_ord, v_ord) { + (Some(h), Some(v)) => (h, v), + _ => return LineDirection::Vertical(Y::Bottom), + }; + match (h, v) { + (Ordering::Less, Ordering::Less) => { + LineDirection::Corner(X::Right, Y::Bottom) + }, + (Ordering::Less, Ordering::Equal) => { + LineDirection::Horizontal(X::Right) + }, + (Ordering::Less, Ordering::Greater) => { + LineDirection::Corner(X::Right, Y::Top) + }, + (Ordering::Equal, Ordering::Greater) => { + LineDirection::Vertical(Y::Top) + }, + (Ordering::Equal, Ordering::Equal) | + (Ordering::Equal, Ordering::Less) => { + LineDirection::Vertical(Y::Bottom) + }, + (Ordering::Greater, Ordering::Less) => { + LineDirection::Corner(X::Left, Y::Bottom) + }, + (Ordering::Greater, Ordering::Equal) => { + LineDirection::Horizontal(X::Left) + }, + (Ordering::Greater, Ordering::Greater) => { + LineDirection::Corner(X::Left, Y::Top) + }, + } + } + } + + impl From for Position { + fn from(point: Point) -> Self { + Self::new(point.horizontal.into(), point.vertical.into()) + } + } + + impl Parse for Point { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + input.try(|i| { + let x = Component::parse(context, i)?; + let y = Component::parse(context, i)?; + + Ok(Self::new(x, y)) + }) + } + } + + impl From> for NumberOrPercentage { + fn from(component: Component) -> Self { + match component { + Component::Center => NumberOrPercentage::Percentage(Percentage(0.5)), + Component::Number(number) => number, + Component::Side(side) => { + let p = Percentage(if side.is_start() { 0. } else { 1. }); + NumberOrPercentage::Percentage(p) + }, + } + } + } + + impl From> for PositionComponent { + fn from(component: Component) -> Self { + match component { + Component::Center => { + PositionComponent::Center + }, + Component::Number(NumberOrPercentage::Number(number)) => { + PositionComponent::Length(Length::from_px(number.value).into()) + }, + Component::Number(NumberOrPercentage::Percentage(p)) => { + PositionComponent::Length(p.into()) + }, + Component::Side(side) => { + PositionComponent::Side(side, None) + }, + } + } + } + + impl Component { + fn partial_cmp(&self, other: &Self) -> Option { + match (NumberOrPercentage::from(*self), NumberOrPercentage::from(*other)) { + (NumberOrPercentage::Percentage(a), NumberOrPercentage::Percentage(b)) => { + a.0.partial_cmp(&b.0) + }, + (NumberOrPercentage::Number(a), NumberOrPercentage::Number(b)) => { + a.value.partial_cmp(&b.value) + }, + (_, _) => { + None + } + } + } + } + + impl Parse for Component { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if let Ok(side) = input.try(|i| S::parse(context, i)) { + return Ok(Component::Side(side)); + } + if let Ok(number) = input.try(|i| NumberOrPercentage::parse(context, i)) { + return Ok(Component::Number(number)); + } + input.try(|i| i.expect_ident_matching("center"))?; + Ok(Component::Center) + } + } + + let ident = input.expect_ident()?; + input.expect_comma()?; + + let (kind, reverse_stops) = match_ignore_ascii_case! { &ident, + "linear" => { + let first = Point::parse(context, input)?; + input.expect_comma()?; + let second = Point::parse(context, input)?; + + let direction = LineDirection::from_points(first, second); + let kind = GenericGradientKind::Linear(direction); + + (kind, false) + }, + "radial" => { + let first_point = Point::parse(context, input)?; + input.expect_comma()?; + let first_radius = Number::parse(context, input)?; + input.expect_comma()?; + let second_point = Point::parse(context, input)?; + input.expect_comma()?; + let second_radius = Number::parse(context, input)?; + + let (reverse_stops, point, radius) = if second_radius.value >= first_radius.value { + (false, second_point, second_radius) + } else { + (true, first_point, first_radius) + }; + + let shape = GenericEndingShape::Circle(Circle::Radius(Length::from_px(radius.value))); + let position = point.into(); + let kind = GenericGradientKind::Radial(shape, position); + + (kind, reverse_stops) + }, + _ => return Err(()), + }; + + let mut items = input.try(|i| { + i.expect_comma()?; + i.parse_comma_separated(|i| { + let function = i.expect_function()?; + let (color, mut p) = i.parse_nested_block(|i| { + let p = match_ignore_ascii_case! { &function, + "color-stop" => { + let p = match NumberOrPercentage::parse(context, i)? { + NumberOrPercentage::Number(number) => number.value, + NumberOrPercentage::Percentage(p) => p.0, + }; + i.expect_comma()?; + p + }, + "from" => 0., + "to" => 1., + _ => return Err(()), + }; + let color = CSSColor::parse(context, i)?; + if color.parsed == Color::CurrentColor { + return Err(()); + } + Ok((color, p)) + })?; + if reverse_stops { + p = 1. - p; + } + Ok(GenericGradientItem::ColorStop(GenericColorStop { + color: color, + position: Some(LengthOrPercentage::Percentage(Percentage(p))), + })) + }) + }).unwrap_or(vec![]); + + if items.is_empty() { + items = vec![ + GenericGradientItem::ColorStop(GenericColorStop { + color: CSSColor::transparent(), + position: Some(Percentage(0.).into()), + }), + GenericGradientItem::ColorStop(GenericColorStop { + color: CSSColor::transparent(), + position: Some(Percentage(1.).into()), + }), + ]; + } else if items.len() == 1 { + let first = items[0].clone(); + items.push(first); + } else { + items.sort_by(|a, b| { + match (a, b) { + (&GenericGradientItem::ColorStop(ref a), &GenericGradientItem::ColorStop(ref b)) => { + match (&a.position, &b.position) { + (&Some(LengthOrPercentage::Percentage(a)), &Some(LengthOrPercentage::Percentage(b))) => { + let ordering = a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal); + if ordering != Ordering::Equal { + return ordering; + } + }, + _ => {}, + } + }, + _ => {}, + } + if reverse_stops { + Ordering::Greater + } else { + Ordering::Less + } + }) + } + + Ok(GenericGradient { + kind: kind, + items: items, + repeating: false, + compat_mode: CompatMode::Modern, + }) + } +} + impl GradientKind { fn parse_linear(context: &ParserContext, input: &mut Parser, @@ -408,13 +660,13 @@ impl Parse for ImageRect { let string = i.expect_url_or_string()?; let url = SpecifiedUrl::parse_from_string(string, context)?; i.expect_comma()?; - let top = NumberOrPercentage::parse(context, i)?; + let top = NumberOrPercentage::parse_non_negative(context, i)?; i.expect_comma()?; - let right = NumberOrPercentage::parse(context, i)?; + let right = NumberOrPercentage::parse_non_negative(context, i)?; i.expect_comma()?; - let bottom = NumberOrPercentage::parse(context, i)?; + let bottom = NumberOrPercentage::parse_non_negative(context, i)?; i.expect_comma()?; - let left = NumberOrPercentage::parse(context, i)?; + let left = NumberOrPercentage::parse_non_negative(context, i)?; Ok(ImageRect { url: url, diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index 27e5494b9f5..7b386297ed2 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -15,7 +15,7 @@ use std::{cmp, fmt, mem}; use std::ascii::AsciiExt; use std::ops::Mul; use style_traits::ToCss; -use style_traits::values::specified::AllowedLengthType; +use style_traits::values::specified::{AllowedLengthType, AllowedNumericType}; use stylesheets::CssRuleType; use super::{AllowQuirks, Number, ToComputedValue}; use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, Normal}; @@ -729,7 +729,10 @@ impl ToCss for Percentage { } impl Percentage { - fn parse_internal(input: &mut Parser, context: AllowedLengthType) -> Result { + /// Parse a specific kind of percentage. + pub fn parse_with_clamping_mode(input: &mut Parser, + context: AllowedNumericType) + -> Result { match try!(input.next()) { Token::Percentage(ref value) if context.is_ok(value.unit_value) => { Ok(Percentage(value.unit_value)) @@ -740,14 +743,14 @@ impl Percentage { /// Parses a percentage token, but rejects it if it's negative. pub fn parse_non_negative(input: &mut Parser) -> Result { - Self::parse_internal(input, AllowedLengthType::NonNegative) + Self::parse_with_clamping_mode(input, AllowedNumericType::NonNegative) } } impl Parse for Percentage { #[inline] fn parse(_context: &ParserContext, input: &mut Parser) -> Result { - Self::parse_internal(input, AllowedLengthType::All) + Self::parse_with_clamping_mode(input, AllowedNumericType::All) } } diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 70e0ea75e8d..0bd876a190d 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -658,7 +658,7 @@ impl ToCss for Number { /// /// Accepts only non-negative numbers. -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] pub enum NumberOrPercentage { @@ -668,13 +668,27 @@ pub enum NumberOrPercentage { no_viewport_percentage!(NumberOrPercentage); -impl Parse for NumberOrPercentage { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if let Ok(per) = input.try(Percentage::parse_non_negative) { +impl NumberOrPercentage { + fn parse_with_clamping_mode(context: &ParserContext, + input: &mut Parser, + type_: AllowedNumericType) + -> Result { + if let Ok(per) = input.try(|i| Percentage::parse_with_clamping_mode(i, type_)) { return Ok(NumberOrPercentage::Percentage(per)); } - Number::parse_non_negative(context, input).map(NumberOrPercentage::Number) + parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number) + } + + /// Parse a non-negative number or percentage. + pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result { + Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative) + } +} + +impl Parse for NumberOrPercentage { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + Self::parse_with_clamping_mode(context, input, AllowedNumericType::All) } } From dd38bba1be0d90cbffca6616c604eec1343d4ff3 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 14 May 2017 00:51:54 +0200 Subject: [PATCH 9/9] Pass the gradient compatibility mode to gecko --- components/style/gecko/conversions.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 91de1643b95..720c1bd2315 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -18,7 +18,7 @@ use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut}; use stylesheets::{Origin, RulesMutateError}; use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image}; use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; -use values::generics::image::{GradientItem, Image as GenericImage}; +use values::generics::image::{CompatMode, Image as GenericImage, GradientItem}; impl From for nsStyleCoord_CalcValue { fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue { @@ -208,7 +208,7 @@ impl nsStyleImage { Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8, gradient.repeating, - /* legacy_syntax = */ false, + gradient.compat_mode == CompatMode::WebKit, stop_count as u32) }; @@ -277,7 +277,7 @@ impl nsStyleImage { Gecko_CreateGradient(gecko_shape, gecko_size, gradient.repeating, - /* legacy_syntax = */ false, + gradient.compat_mode == CompatMode::WebKit, stop_count as u32) };