diff --git a/components/style/gecko/url.rs b/components/style/gecko/url.rs index ac7c9359b00..00f4ed8266b 100644 --- a/components/style/gecko/url.rs +++ b/components/style/gecko/url.rs @@ -285,14 +285,15 @@ impl SpecifiedImageUrl { /// Provides an alternate method for parsing that associates the URL /// with anonymous CORS headers. - pub fn parse_with_cors_anonymous<'i, 't>( + pub fn parse_with_cors_mode<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, + cors_mode: CorsMode, ) -> Result> { Ok(SpecifiedImageUrl(SpecifiedUrl::parse_with_cors_mode( context, input, - CorsMode::Anonymous, + cors_mode, )?)) } } diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 4eda2e91e21..6f609bdf6fa 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -15,7 +15,7 @@ use crate::values::computed::NumberOrPercentage; use crate::values::computed::{Angle, Color, Context}; use crate::values::computed::{ AngleOrPercentage, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, - ToComputedValue, + ToComputedValue, Resolution, }; use crate::values::generics::image::{self as generic, GradientCompatMode}; use crate::values::specified::image::LineDirection as SpecifiedLineDirection; @@ -26,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. /// diff --git a/components/style/values/computed/resolution.rs b/components/style/values/computed/resolution.rs index c08fe2be1a3..72580635b81 100644 --- a/components/style/values/computed/resolution.rs +++ b/components/style/values/computed/resolution.rs @@ -13,6 +13,8 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; /// A computed ``. +#[repr(C)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)] pub struct Resolution(CSSFloat); impl Resolution { diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index f92eca8ae2f..ff6498d668a 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. @@ -51,6 +51,9 @@ pub enum GenericImage { /// and store images directly inside of cross-fade instead of /// boxing them there. CrossFade(Box>), + + /// An `image-set()` function. + ImageSet(Box>), } pub use self::GenericImage as Image; @@ -135,6 +138,36 @@ pub use self::GenericCrossFade as CrossFade; pub use self::GenericCrossFadeElement as CrossFadeElement; pub use self::GenericCrossFadeImage as CrossFadeImage; +/// https://drafts.csswg.org/css-images-4/#image-set-notation +#[css(comma, function = "image-set")] +#[derive( + Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue, +)] +#[repr(C)] +pub struct GenericImageSet { + /// All of the image and resolution pairs. + #[css(iterable)] + pub items: crate::OwnedSlice>, +} + +/// An optional percent and a cross fade image. +#[derive( + Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss, +)] +#[repr(C)] +pub struct GenericImageSetItem { + /// ``. `` is converted to `Image::Url` at parse time. + pub image: Image, + /// The ``. + /// + /// TODO: Skip serialization if it is 1x. + pub resolution: Resolution, + // TODO: type() function. +} + +pub use self::GenericImageSet as ImageSet; +pub use self::GenericImageSetItem as ImageSetItem; + /// A CSS gradient. /// #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] @@ -377,26 +410,23 @@ 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, + Image: 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, + Resolution: ToCss, { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where @@ -415,6 +445,7 @@ where serialize_atom_identifier(selector, dest)?; dest.write_str(")") }, + Image::ImageSet(ref is) => is.to_css(dest), 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 86b01cb5a31..1a0711231b3 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -9,6 +9,7 @@ use crate::custom_properties::SpecifiedValue; use crate::parser::{Parse, ParserContext}; +use crate::stylesheets::CorsMode; use crate::values::generics::image::PaintWorklet; use crate::values::generics::image::{ self as generic, Circle, Ellipse, GradientCompatMode, ShapeExtent, @@ -20,7 +21,7 @@ use crate::values::specified::position::{Position, PositionComponent, Side}; use crate::values::specified::url::SpecifiedImageUrl; use crate::values::specified::{ Angle, AngleOrPercentage, Color, Length, LengthPercentage, NonNegativeLength, - NonNegativeLengthPercentage, + NonNegativeLengthPercentage, Resolution }; use crate::values::specified::{Number, NumberOrPercentage, Percentage}; use crate::Atom; @@ -35,7 +36,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. /// @@ -61,6 +62,12 @@ pub type CrossFadeImage = generic::CrossFadeImage; /// A specified percentage or nothing. pub type PercentOrNone = generic::PercentOrNone; +/// `image-set()` +pub type ImageSet = generic::ImageSet; + +/// Each of the arguments to `image-set()` +pub type ImageSetItem = generic::ImageSetItem; + type LengthPercentageItemList = crate::OwnedSlice>; #[cfg(feature = "gecko")] @@ -73,6 +80,16 @@ fn cross_fade_enabled() -> bool { false } +#[cfg(feature = "gecko")] +fn image_set_enabled() -> bool { + static_prefs::pref!("layout.css.image-set.enabled") +} + +#[cfg(feature = "servo")] +fn image_set_enabled() -> bool { + false +} + impl SpecifiedValueInfo for Gradient { const SUPPORTED_TYPES: u8 = CssType::GRADIENT; @@ -110,6 +127,16 @@ impl SpecifiedValueInfo for generic::CrossFade SpecifiedValueInfo for generic::ImageSet { + const SUPPORTED_TYPES: u8 = 0; + + fn collect_completion_keywords(f: KeywordsCollectFn) { + if image_set_enabled() { + f(&["image-set"]); + } + } +} + /// A specified gradient line direction. /// /// FIXME(emilio): This should be generic over Angle. @@ -152,18 +179,33 @@ impl Parse for Image { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, + ) -> Result> { + Image::parse_with_cors_mode(context, input, CorsMode::None) + } +} + +impl Image { + fn parse_with_cors_mode<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + cors_mode: CorsMode, ) -> Result> { if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { return Ok(generic::Image::None); } - if let Ok(url) = input.try_parse(|input| SpecifiedImageUrl::parse(context, input)) { + if let Ok(url) = input.try_parse(|input| SpecifiedImageUrl::parse_with_cors_mode(context, input, cors_mode)) { return Ok(generic::Image::Url(url)); } if let Ok(gradient) = input.try_parse(|i| Gradient::parse(context, i)) { return Ok(generic::Image::Gradient(Box::new(gradient))); } + if image_set_enabled() { + if let Ok(is) = input.try_parse(|input| ImageSet::parse(context, input, cors_mode)) { + return Ok(generic::Image::ImageSet(Box::new(is))); + } + } if cross_fade_enabled() { - if let Ok(cf) = input.try_parse(|input| CrossFade::parse(context, input)) { + if let Ok(cf) = input.try_parse(|input| CrossFade::parse(context, input, cors_mode)) { return Ok(generic::Image::CrossFade(Box::new(cf))); } } @@ -175,7 +217,7 @@ impl Parse for Image { } #[cfg(feature = "gecko")] { - if let Ok(image_rect) = input.try_parse(|input| MozImageRect::parse(context, input)) { + if let Ok(image_rect) = input.try_parse(|input| MozImageRect::parse(context, input, cors_mode)) { return Ok(generic::Image::Rect(Box::new(image_rect))); } Ok(generic::Image::Element(Image::parse_element(input)?)) @@ -214,40 +256,37 @@ impl Image { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { - if let Ok(url) = - input.try_parse(|input| SpecifiedImageUrl::parse_with_cors_anonymous(context, input)) - { - return Ok(generic::Image::Url(url)); - } - Self::parse(context, input) + Self::parse_with_cors_mode(context, input, CorsMode::Anonymous) } } -impl Parse for CrossFade { +impl CrossFade { /// cross-fade() = cross-fade( # ) fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, + cors_mode: CorsMode, ) -> Result> { input.expect_function_matching("cross-fade")?; let elements = input.parse_nested_block(|input| { - input.parse_comma_separated(|input| CrossFadeElement::parse(context, input)) + input.parse_comma_separated(|input| CrossFadeElement::parse(context, input, cors_mode)) })?; let elements = crate::OwnedSlice::from(elements); Ok(Self { elements }) } } -impl Parse for CrossFadeElement { +impl CrossFadeElement { /// = ? && [ | ] fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, + cors_mode: CorsMode, ) -> 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)?; + let image = CrossFadeImage::parse(context, input, cors_mode)?; // Try and parse a trailing percent sign. if percent == PercentOrNone::None { percent = PercentOrNone::parse_or_none(context, input); @@ -274,6 +313,40 @@ impl PercentOrNone { } } +impl ImageSet { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + cors_mode: CorsMode, + ) -> Result> { + input.expect_function_matching("image-set")?; + let items = input.parse_nested_block(|input| { + input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode)) + })?; + Ok(Self { + items: items.into() + }) + } +} + +impl ImageSetItem { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + cors_mode: CorsMode, + ) -> Result> { + let image = match input.try_parse(|i| i.expect_url_or_string()) { + Ok(url) => Image::Url(SpecifiedImageUrl::parse_from_string(url.as_ref().into(), context, cors_mode)), + Err(..) => Image::parse(context, input)?, + }; + let resolution = input.try_parse(|input| Resolution::parse(context, input)).unwrap_or(Resolution::X(1.0)); + Ok(Self { + image, + resolution, + }) + } +} + impl Parse for Gradient { fn parse<'i, 't>( context: &ParserContext, @@ -1043,11 +1116,12 @@ impl Parse for PaintWorklet { } } -impl Parse for MozImageRect { +impl MozImageRect { #[cfg(not(feature = "gecko"))] fn parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, + _cors_mode: CorsMode, ) -> Result> { Err(input.new_error_for_next_token()) } @@ -1056,6 +1130,7 @@ impl Parse for MozImageRect { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, + cors_mode: CorsMode, ) -> Result> { input.try_parse(|i| i.expect_function_matching("-moz-image-rect"))?; input.parse_nested_block(|i| { @@ -1063,7 +1138,7 @@ impl Parse for MozImageRect { let url = SpecifiedImageUrl::parse_from_string( string.as_ref().to_owned(), context, - crate::stylesheets::CorsMode::None, + cors_mode, ); i.expect_comma()?; let top = NumberOrPercentage::parse_non_negative(context, i)?; diff --git a/components/style/values/specified/resolution.rs b/components/style/values/specified/resolution.rs index f8854226b53..e8674cffaaa 100644 --- a/components/style/values/specified/resolution.rs +++ b/components/style/values/specified/resolution.rs @@ -12,7 +12,7 @@ use cssparser::{Parser, Token}; use style_traits::{ParseError, StyleParseErrorKind}; /// A specified resolution. -#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, SpecifiedValueInfo)] pub enum Resolution { /// Dots per inch. #[css(dimension)]