Bug 1695402 - Support image-set() on the cursor property. r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D106745
This commit is contained in:
Micah Tigley 2021-03-03 18:34:46 +00:00
parent 043c0bbe2d
commit 99ac1f352b
9 changed files with 65 additions and 31 deletions

View File

@ -5831,7 +5831,6 @@ exports.CSS_PROPERTIES = {
"sw-resize",
"text",
"unset",
"url",
"vertical-text",
"w-resize",
"wait",

View File

@ -3944,8 +3944,10 @@ static CursorImage ComputeCustomCursor(nsPresContext* aPresContext,
// consumer know.
bool loading = false;
for (const auto& image : style.StyleUI()->mCursor.images.AsSpan()) {
MOZ_ASSERT(image.image.IsImageRequestType(),
"Cursor image should only parse url() type");
uint32_t status;
imgRequestProxy* req = image.url.GetImage();
imgRequestProxy* req = image.image.GetImageRequest();
if (!req || NS_FAILED(req->GetImageStatus(&status))) {
continue;
}

View File

@ -659,7 +659,7 @@ static void CollectImageURLsForProperty(nsCSSPropertyID aProp,
switch (aProp) {
case eCSSProperty_cursor:
for (auto& image : aStyle.StyleUI()->mCursor.images.AsSpan()) {
AddImageURL(image.url, aURLs);
AddImageURL(image.image, aURLs);
}
break;
case eCSSProperty_background_image:

View File

@ -3055,13 +3055,10 @@ void nsStyleUI::TriggerImageLoads(Document& aDocument,
: Span<const StyleCursorImage>();
for (size_t i = 0; i < cursorImages.Length(); ++i) {
auto& cursor = cursorImages[i];
if (!cursor.url.IsImageResolved()) {
const auto* oldCursor =
oldCursorImages.Length() > i ? &oldCursorImages[i] : nullptr;
const_cast<StyleComputedImageUrl&>(cursor.url)
.ResolveImage(aDocument, oldCursor ? &oldCursor->url : nullptr);
}
const auto* oldCursorImage =
oldCursorImages.Length() > i ? &oldCursorImages[i].image : nullptr;
const_cast<StyleCursorImage&>(cursor).image.ResolveImage(aDocument,
oldCursorImage);
}
}

View File

@ -5628,8 +5628,15 @@ var gCSSProperties = {
"url(foo.png), url(bar.png) 7 2, wait",
"url(foo.png) 3 2, url(bar.png) 7 9, pointer",
"url(foo.png) calc(1 + 2) calc(3), pointer",
"image-set(url(foo.png)), auto",
],
invalid_values: [
"url(foo.png)",
"url(foo.png) 5 5",
"image-set(linear-gradient(red, blue)), auto",
// Gradients are supported per spec, but we don't have support for it yet
"linear-gradient(red, blue), auto",
],
invalid_values: ["url(foo.png)", "url(foo.png) 5 5"],
},
direction: {
domProp: "direction",

View File

@ -5,7 +5,7 @@
//! Computed values for UI properties
use crate::values::computed::color::Color;
use crate::values::computed::url::ComputedImageUrl;
use crate::values::computed::image::Image;
use crate::values::computed::Number;
use crate::values::generics::ui as generics;
@ -16,7 +16,7 @@ pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
pub type Cursor = generics::GenericCursor<CursorImage>;
/// A computed value for item of `image cursors`.
pub type CursorImage = generics::GenericCursorImage<ComputedImageUrl, Number>;
pub type CursorImage = generics::GenericCursorImage<Image, Number>;
/// A computed value for `scrollbar-color` property.
pub type ScrollbarColor = generics::GenericScrollbarColor<Color>;

View File

@ -61,15 +61,14 @@ impl<Image: ToCss> ToCss for Cursor<Image> {
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct GenericCursorImage<ImageUrl, Number> {
pub struct GenericCursorImage<Image, Number> {
/// The url to parse images from.
pub url: ImageUrl,
pub image: Image,
/// Whether the image has a hotspot or not.
pub has_hotspot: bool,
/// The x coordinate.
@ -80,12 +79,12 @@ pub struct GenericCursorImage<ImageUrl, Number> {
pub use self::GenericCursorImage as CursorImage;
impl<ImageUrl: ToCss, Number: ToCss> ToCss for CursorImage<ImageUrl, Number> {
impl<Image: ToCss, Number: ToCss> ToCss for CursorImage<Image, Number> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.url.to_css(dest)?;
self.image.to_css(dest)?;
if self.has_hotspot {
dest.write_str(" ")?;
self.hotspot_x.to_css(dest)?;

View File

@ -181,7 +181,7 @@ impl Parse for Image {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Image, ParseError<'i>> {
Image::parse_with_cors_mode(context, input, CorsMode::None, /* allow_none = */ true)
Image::parse_with_cors_mode(context, input, CorsMode::None, /* allow_none = */ true, /* only_url = */ false)
}
}
@ -191,23 +191,32 @@ impl Image {
input: &mut Parser<'i, 't>,
cors_mode: CorsMode,
allow_none: bool,
only_url: bool,
) -> Result<Image, ParseError<'i>> {
if allow_none && 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_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)) {
if let Ok(is) = input.try_parse(|input| ImageSet::parse(context, input, cors_mode, only_url)) {
return Ok(generic::Image::ImageSet(Box::new(is)));
}
}
if only_url {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
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, cors_mode)) {
return Ok(generic::Image::CrossFade(Box::new(cf)));
@ -264,6 +273,21 @@ impl Image {
input,
CorsMode::Anonymous,
/* allow_none = */ true,
/* only_url = */ false,
)
}
/// Provides an alternate method for parsing, but only for urls.
pub fn parse_only_url<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Image, ParseError<'i>> {
Self::parse_with_cors_mode(
context,
input,
CorsMode::None,
/* allow_none = */ false,
/* only_url = */ true,
)
}
}
@ -310,7 +334,7 @@ impl CrossFadeImage {
cors_mode: CorsMode,
) -> Result<Self, ParseError<'i>> {
if let Ok(image) = input.try_parse(|input| {
Image::parse_with_cors_mode(context, input, cors_mode, /* allow_none = */ false)
Image::parse_with_cors_mode(context, input, cors_mode, /* allow_none = */ false, /* only_url = */ false)
}) {
return Ok(Self::Image(image));
}
@ -339,10 +363,11 @@ impl ImageSet {
context: &ParserContext,
input: &mut Parser<'i, 't>,
cors_mode: CorsMode,
only_url: bool,
) -> 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))
input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode, only_url))
})?;
Ok(Self {
selected_index: 0,
@ -356,6 +381,7 @@ impl ImageSetItem {
context: &ParserContext,
input: &mut Parser<'i, 't>,
cors_mode: CorsMode,
only_url: bool,
) -> Result<Self, ParseError<'i>> {
let image = match input.try_parse(|i| i.expect_url_or_string()) {
Ok(url) => Image::Url(SpecifiedImageUrl::parse_from_string(
@ -364,7 +390,7 @@ impl ImageSetItem {
cors_mode,
)),
Err(..) => Image::parse_with_cors_mode(
context, input, cors_mode, /* allow_none = */ false,
context, input, cors_mode, /* allow_none = */ false, /* only_url = */ only_url
)?,
};
let resolution = input

View File

@ -7,17 +7,17 @@
use crate::parser::{Parse, ParserContext};
use crate::values::generics::ui as generics;
use crate::values::specified::color::Color;
use crate::values::specified::url::SpecifiedImageUrl;
use crate::values::specified::image::Image;
use crate::values::specified::Number;
use cssparser::Parser;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
/// A specified value for the `cursor` property.
pub type Cursor = generics::GenericCursor<CursorImage>;
/// A specified value for item of `image cursors`.
pub type CursorImage = generics::GenericCursorImage<SpecifiedImageUrl, Number>;
pub type CursorImage = generics::GenericCursorImage<Image, Number>;
impl Parse for Cursor {
/// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
@ -47,7 +47,7 @@ impl Parse for CursorImage {
) -> Result<Self, ParseError<'i>> {
use crate::Zero;
let url = SpecifiedImageUrl::parse(context, input)?;
let image = Image::parse_only_url(context, input)?;
let mut has_hotspot = false;
let mut hotspot_x = Number::zero();
let mut hotspot_y = Number::zero();
@ -59,7 +59,7 @@ impl Parse for CursorImage {
}
Ok(Self {
url,
image,
has_hotspot,
hotspot_x,
hotspot_y,
@ -67,6 +67,10 @@ impl Parse for CursorImage {
}
}
// This trait is manually implemented because we don't support the whole <image>
// syntax for cursors
impl SpecifiedValueInfo for CursorImage {}
/// Specified value of `-moz-force-broken-image-icon`
#[derive(
Clone,