From 8f89ebffec5fbd85979a9078b93b4a1da5efe47b Mon Sep 17 00:00:00 2001 From: Zeke Medley Date: Fri, 31 Jul 2020 16:11:38 +0000 Subject: [PATCH] style: Add parsing for cross-fade from CSS Image Values and Replaced Content Module Level 4. This is the first of what will likely be a couple patches for cross-fade's implementation. Bug 546052 tracks it's complete implementation. Differential Revision: https://phabricator.services.mozilla.com/D81889 --- components/style/parser.rs | 12 +++ components/style/values/computed/image.rs | 9 +- components/style/values/generics/image.rs | 97 +++++++++++++++++++++- components/style/values/specified/image.rs | 91 +++++++++++++++++++- 4 files changed, 204 insertions(+), 5 deletions(-) diff --git a/components/style/parser.rs b/components/style/parser.rs index 26e7cab1ad9..81effece0a6 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -207,6 +207,18 @@ where } } +impl Parse for Box +where + T: Parse +{ + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + T::parse(context, input).map(Box::new) + } +} + impl Parse for crate::OwnedStr { fn parse<'i, 't>( _: &ParserContext, diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index a2b59809021..4eda2e91e21 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -8,6 +8,7 @@ //! [image]: https://drafts.csswg.org/css-images/#image-values use crate::values::computed::position::Position; +use crate::values::computed::percentage::Percentage; use crate::values::computed::url::ComputedImageUrl; #[cfg(feature = "gecko")] use crate::values::computed::NumberOrPercentage; @@ -25,7 +26,7 @@ use style_traits::{CssWriter, ToCss}; /// Computed values for an image according to CSS-IMAGES. /// -pub type Image = generic::GenericImage; +pub type Image = generic::GenericImage; /// Computed values for a CSS gradient. /// @@ -40,6 +41,12 @@ pub type Gradient = generic::GenericGradient< Color, >; +/// Computed values for CSS cross-fade +/// +pub type CrossFade = generic::CrossFade; +/// A computed percentage or nothing. +pub type PercentOrNone = generic::PercentOrNone; + /// A computed radial gradient ending shape. pub type EndingShape = generic::GenericEndingShape; diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index 9376cad2c2e..f92eca8ae2f 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -22,7 +22,7 @@ use style_traits::{CssWriter, ToCss}; Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C, u8)] -pub enum GenericImage { +pub enum GenericImage { /// `none` variant. None, /// A `` image. @@ -45,10 +45,96 @@ pub enum GenericImage { /// #[cfg(feature = "servo-layout-2013")] PaintWorklet(PaintWorklet), + + /// A `` image. Storing this directly inside of + /// GenericImage increases the size by 8 bytes so we box it here + /// and store images directly inside of cross-fade instead of + /// boxing them there. + CrossFade(Box>), } pub use self::GenericImage as Image; +/// +#[css(comma, function = "cross-fade")] +#[derive( + Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue, +)] +#[repr(C)] +pub struct GenericCrossFade { + /// All of the image percent pairings passed as arguments to + /// cross-fade. + #[css(iterable)] + pub elements: crate::OwnedSlice>, +} + +/// A ` | none` value. Represents optional percentage values +/// assosicated with cross-fade images. +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + ToComputedValue, + ToResolvedValue, + ToShmem, + ToCss, +)] +#[repr(C, u8)] +pub enum PercentOrNone { + /// `none` variant. + #[css(skip)] + None, + /// A percentage variant. + Percent(Percentage), +} + +/// An optional percent and a cross fade image. +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + ToComputedValue, + ToResolvedValue, + ToShmem, + ToCss, +)] +#[repr(C)] +pub struct GenericCrossFadeElement { + /// The percent of the final image that `image` will be. + pub percent: PercentOrNone, + /// A color or image that will be blended when cross-fade is + /// evaluated. + pub image: GenericCrossFadeImage, +} + +/// An image or a color. `cross-fade` takes either when blending +/// images together. +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + ToComputedValue, + ToResolvedValue, + ToShmem, + ToCss, + Parse, +)] +#[repr(C, u8)] +pub enum GenericCrossFadeImage { + /// A boxed image value. Boxing provides indirection so images can + /// be cross-fades and cross-fades can be images. + Image(I), + /// A color value. + Color(C), +} + +pub use self::GenericCrossFade as CrossFade; +pub use self::GenericCrossFadeElement as CrossFadeElement; +pub use self::GenericCrossFadeImage as CrossFadeImage; + /// A CSS gradient. /// #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] @@ -291,22 +377,26 @@ pub struct GenericMozImageRect { pub use self::GenericMozImageRect as MozImageRect; -impl fmt::Debug for Image +impl fmt::Debug for Image where G: ToCss, R: ToCss, U: ToCss, + C: ToCss, + P: ToCss, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(&mut CssWriter::new(f)) } } -impl ToCss for Image +impl ToCss for Image where G: ToCss, R: ToCss, U: ToCss, + C: ToCss, + P: ToCss, { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where @@ -325,6 +415,7 @@ where serialize_atom_identifier(selector, dest)?; dest.write_str(")") }, + Image::CrossFade(ref cf) => cf.to_css(dest), } } } diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 088a4ace687..684fa3e2bab 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -35,7 +35,7 @@ use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; /// Specified values for an image according to CSS-IMAGES. /// -pub type Image = generic::Image; +pub type Image = generic::Image; /// Specified values for a CSS gradient. /// @@ -50,6 +50,17 @@ pub type Gradient = generic::Gradient< Color, >; +/// Specified values for CSS cross-fade +/// cross-fade( CrossFadeElement, ...) +/// +pub type CrossFade = generic::CrossFade; +/// CrossFadeElement = percent? CrossFadeImage +pub type CrossFadeElement = generic::CrossFadeElement; +/// CrossFadeImage = image | color +pub type CrossFadeImage = generic::CrossFadeImage; +/// A specified percentage or nothing. +pub type PercentOrNone = generic::PercentOrNone; + type LengthPercentageItemList = crate::OwnedSlice>; #[cfg(feature = "gecko")] @@ -62,6 +73,16 @@ fn conic_gradients_enabled() -> bool { false } +#[cfg(feature = "gecko")] +fn cross_fade_enabled() -> bool { + static_prefs::pref!("layout.css.cross-fade.enabled") +} + +#[cfg(feature = "servo")] +fn cross_fade_enabled() -> bool { + false +} + impl SpecifiedValueInfo for Gradient { const SUPPORTED_TYPES: u8 = CssType::GRADIENT; @@ -89,6 +110,18 @@ impl SpecifiedValueInfo for Gradient { } } +// Need to manually implement as whether or not cross-fade shows up in +// completions & etc is dependent on it being enabled. +impl SpecifiedValueInfo for generic::CrossFade { + const SUPPORTED_TYPES: u8 = 0; + + fn collect_completion_keywords(f: KeywordsCollectFn) { + if cross_fade_enabled() { + f(&["cross-fade"]); + } + } +} + /// A specified gradient line direction. /// /// FIXME(emilio): This should be generic over Angle. @@ -141,6 +174,11 @@ impl Parse for Image { if let Ok(gradient) = input.try_parse(|i| Gradient::parse(context, i)) { return Ok(generic::Image::Gradient(Box::new(gradient))); } + if cross_fade_enabled() { + if let Ok(cf) = input.try_parse(|input| CrossFade::parse(context, input)) { + return Ok(generic::Image::CrossFade(Box::new(cf))); + } + } #[cfg(feature = "servo-layout-2013")] { if let Ok(paint_worklet) = input.try_parse(|i| PaintWorklet::parse(context, i)) { @@ -197,6 +235,57 @@ impl Image { } } +impl Parse for CrossFade { + /// cross-fade() = cross-fade( # ) + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + input.expect_function_matching("cross-fade")?; + let elements = input.parse_nested_block(|input| { + input.parse_comma_separated(|input| CrossFadeElement::parse(context, input)) + })?; + let elements = crate::OwnedSlice::from(elements); + Ok(Self { elements }) + } +} + +impl Parse for CrossFadeElement { + /// = ? && [ | ] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + // Try and parse a leading percent sign. + let mut percent = PercentOrNone::parse_or_none(context, input); + // Parse the image + let image = CrossFadeImage::parse(context, input)?; + // Try and parse a trailing percent sign. + if percent == PercentOrNone::None { + percent = PercentOrNone::parse_or_none(context, input); + } + Ok(Self { percent, image }) + } +} + +impl PercentOrNone { + fn parse_or_none<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Self { + // We clamp our values here as this is the way that Safari and + // Chrome's implementation handle out-of-bounds percentages + // but whether or not this behavior follows the specification + // is still being discussed. See: + // + if let Ok(percent) = input.try_parse(|input| Percentage::parse_non_negative(context, input)) { + Self::Percent(percent.clamp_to_hundred()) + } else { + Self::None + } + } +} + impl Parse for Gradient { fn parse<'i, 't>( context: &ParserContext,