mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
style: Implement parsing and serialization for most of image-set().
This implements the basic image-set notation without the format() function (for simplicity). There's a remaining serialization issue (we should probably skip 1x resolutions), but that's fine for now, I'll address this in a follow-up when the feature is testable. The intention is to do the image selection at computed value time (keeping a selected index or such), but same, follow-up. This also fixes an issue where the cors-mode for -moz-image-rect and cross-fade() was getting ignored when parsing. Differential Revision: https://phabricator.services.mozilla.com/D100640
This commit is contained in:
parent
f27003c810
commit
4f28a8cd31
6 changed files with 139 additions and 30 deletions
|
@ -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<Self, ParseError<'i>> {
|
||||
Ok(SpecifiedImageUrl(SpecifiedUrl::parse_with_cors_mode(
|
||||
context,
|
||||
input,
|
||||
CorsMode::Anonymous,
|
||||
cors_mode,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
/// <https://drafts.csswg.org/css-images/#image-values>
|
||||
pub type Image = generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl, Color, Percentage>;
|
||||
pub type Image = generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl, Color, Percentage, Resolution>;
|
||||
|
||||
/// Computed values for a CSS gradient.
|
||||
/// <https://drafts.csswg.org/css-images/#gradients>
|
||||
|
|
|
@ -13,6 +13,8 @@ use std::fmt::{self, Write};
|
|||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
/// A computed `<resolution>`.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
|
||||
pub struct Resolution(CSSFloat);
|
||||
|
||||
impl Resolution {
|
||||
|
|
|
@ -22,7 +22,7 @@ use style_traits::{CssWriter, ToCss};
|
|||
Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
#[repr(C, u8)]
|
||||
pub enum GenericImage<G, MozImageRect, ImageUrl, Color, Percentage> {
|
||||
pub enum GenericImage<G, MozImageRect, ImageUrl, Color, Percentage, Resolution> {
|
||||
/// `none` variant.
|
||||
None,
|
||||
/// A `<url()>` image.
|
||||
|
@ -51,6 +51,9 @@ pub enum GenericImage<G, MozImageRect, ImageUrl, Color, Percentage> {
|
|||
/// and store images directly inside of cross-fade instead of
|
||||
/// boxing them there.
|
||||
CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
|
||||
|
||||
/// An `image-set()` function.
|
||||
ImageSet(Box<GenericImageSet<Self, Resolution>>),
|
||||
}
|
||||
|
||||
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<Image, Resolution> {
|
||||
/// All of the image and resolution pairs.
|
||||
#[css(iterable)]
|
||||
pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
|
||||
}
|
||||
|
||||
/// An optional percent and a cross fade image.
|
||||
#[derive(
|
||||
Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct GenericImageSetItem<Image, Resolution> {
|
||||
/// `<image>`. `<string>` is converted to `Image::Url` at parse time.
|
||||
pub image: Image,
|
||||
/// The `<resolution>`.
|
||||
///
|
||||
/// 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.
|
||||
/// <https://drafts.csswg.org/css-images/#gradients>
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||
|
@ -377,26 +410,23 @@ pub struct GenericMozImageRect<NumberOrPercentage, MozImageRectUrl> {
|
|||
|
||||
pub use self::GenericMozImageRect as MozImageRect;
|
||||
|
||||
impl<G, R, U, C, P> fmt::Debug for Image<G, R, U, C, P>
|
||||
impl<G, R, U, C, P, Resolution> fmt::Debug for Image<G, R, U, C, P, Resolution>
|
||||
where
|
||||
G: ToCss,
|
||||
R: ToCss,
|
||||
U: ToCss,
|
||||
C: ToCss,
|
||||
P: ToCss,
|
||||
Image<G, R, U, C, P, Resolution>: ToCss,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.to_css(&mut CssWriter::new(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, R, U, C, P> ToCss for Image<G, R, U, C, P>
|
||||
impl<G, R, U, C, P, Resolution> ToCss for Image<G, R, U, C, P, Resolution>
|
||||
where
|
||||
G: ToCss,
|
||||
R: ToCss,
|
||||
U: ToCss,
|
||||
C: ToCss,
|
||||
P: ToCss,
|
||||
Resolution: ToCss,
|
||||
{
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> 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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
/// <https://drafts.csswg.org/css-images/#image-values>
|
||||
pub type Image = generic::Image<Gradient, MozImageRect, SpecifiedImageUrl, Color, Percentage>;
|
||||
pub type Image = generic::Image<Gradient, MozImageRect, SpecifiedImageUrl, Color, Percentage, Resolution>;
|
||||
|
||||
/// Specified values for a CSS gradient.
|
||||
/// <https://drafts.csswg.org/css-images/#gradients>
|
||||
|
@ -61,6 +62,12 @@ pub type CrossFadeImage = generic::CrossFadeImage<Image, Color>;
|
|||
/// A specified percentage or nothing.
|
||||
pub type PercentOrNone = generic::PercentOrNone<Percentage>;
|
||||
|
||||
/// `image-set()`
|
||||
pub type ImageSet = generic::ImageSet<Image, Resolution>;
|
||||
|
||||
/// Each of the arguments to `image-set()`
|
||||
pub type ImageSetItem = generic::ImageSetItem<Image, Resolution>;
|
||||
|
||||
type LengthPercentageItemList = crate::OwnedSlice<generic::GradientItem<Color, LengthPercentage>>;
|
||||
|
||||
#[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<Image, Color, Percentage> SpecifiedValueInfo for generic::CrossFade<Image,
|
|||
}
|
||||
}
|
||||
|
||||
impl<Image, Resolution> SpecifiedValueInfo for generic::ImageSet<Image, Resolution> {
|
||||
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, ParseError<'i>> {
|
||||
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<Image, ParseError<'i>> {
|
||||
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<Image, ParseError<'i>> {
|
||||
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( <cf-image># )
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
cors_mode: CorsMode,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
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 {
|
||||
/// <cf-image> = <percentage>? && [ <image> | <color> ]
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
cors_mode: CorsMode,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
// 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<Self, ParseError<'i>> {
|
||||
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<Self, ParseError<'i>> {
|
||||
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<Self, ParseError<'i>> {
|
||||
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<Self, ParseError<'i>> {
|
||||
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)?;
|
||||
|
|
|
@ -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)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue