diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index 08ef628f1ff..5d960a8e9e1 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -432,4 +432,11 @@ impl Device { }; SideOffsets2D::new(top, right, bottom, left) } + + /// Returns true if the given MIME type is supported + pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { + unsafe { + bindings::Gecko_IsSupportedImageMimeType(mime_type.as_ptr(), mime_type.len() as u32) + } + } } diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 5e123a07abb..6290ac68a5d 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -74,9 +74,20 @@ impl ToComputedValue for specified::ImageSet { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { let items = self.items.to_computed_value(context); let dpr = context.device().device_pixel_ratio().get(); + + // If no item have a supported MIME type, the behavior is undefined by the standard + // By default, we select the first item + let mut supported_image = false; let mut selected_index = 0; let mut selected_resolution = items[0].resolution.dppx(); - for (i, item) in items.iter().enumerate().skip(1) { + + for (i, item) in items.iter().enumerate() { + + // If the MIME type is not supported, we discard the ImageSetItem + if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) { + continue; + } + let candidate_resolution = item.resolution.dppx(); // https://drafts.csswg.org/css-images-4/#image-set-notation: @@ -97,7 +108,9 @@ impl ToComputedValue for specified::ImageSet { false }; - if better_candidate() { + // The first item with a supported MIME type is obviously the current best candidate + if !supported_image || better_candidate() { + supported_image = true; selected_index = i; selected_resolution = candidate_resolution; } diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index d49a8a2b4de..014ae46db02 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -132,7 +132,7 @@ pub struct GenericImageSet { /// An optional percent and a cross fade image. #[derive( - Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss, + Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C)] pub struct GenericImageSetItem { @@ -142,7 +142,33 @@ pub struct GenericImageSetItem { /// /// TODO: Skip serialization if it is 1x. pub resolution: Resolution, - // TODO: type() function. + + /// The `type()` + /// (Optional) Specify the image's MIME type + pub mime_type: crate::OwnedStr, + + /// True if mime_type has been specified + pub has_mime_type: bool, +} + +impl ToCss for GenericImageSetItem +{ + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write, + { + self.image.to_css(dest)?; + dest.write_str(" ")?; + self.resolution.to_css(dest)?; + + if self.has_mime_type { + dest.write_str(" ")?; + dest.write_str("type(")?; + self.mime_type.to_css(dest)?; + dest.write_str(")")?; + } + Ok(()) + } } pub use self::GenericImageSet as ImageSet; diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 0be0fcd2a0c..85c178e064d 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -377,6 +377,13 @@ impl ImageSet { } impl ImageSetItem { + fn parse_type<'i>(p: &mut Parser<'i, '_>) -> Result> { + p.expect_function_matching("type")?; + p.parse_nested_block(|input| { + Ok(input.expect_string()?.as_ref().to_owned().into()) + }) + } + fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, @@ -393,10 +400,20 @@ impl ImageSetItem { context, input, cors_mode, /* allow_none = */ false, /* only_url = */ only_url )?, }; - let resolution = input - .try_parse(|input| Resolution::parse(context, input)) - .unwrap_or(Resolution::X(1.0)); - Ok(Self { image, resolution }) + + let mut resolution = input.try_parse(|input| Resolution::parse(context, input)).ok(); + let mime_type = input.try_parse(Self::parse_type).ok(); + + // Try to parse resolution after type(). + if mime_type.is_some() && resolution.is_none() { + resolution = input.try_parse(|input| Resolution::parse(context, input)).ok(); + } + + let resolution = resolution.unwrap_or(Resolution::X(1.0)); + let has_mime_type = mime_type.is_some(); + let mime_type = mime_type.unwrap_or_default(); + + Ok(Self { image, resolution, has_mime_type, mime_type }) } }