diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 6f609bdf6fa..307b7fa0c2f 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -18,7 +18,7 @@ use crate::values::computed::{ ToComputedValue, Resolution, }; use crate::values::generics::image::{self as generic, GradientCompatMode}; -use crate::values::specified::image::LineDirection as SpecifiedLineDirection; +use crate::values::specified::image as specified; use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword}; use std::f32::consts::PI; use std::fmt::{self, Write}; @@ -64,13 +64,66 @@ pub enum LineDirection { Corner(HorizontalPositionKeyword, VerticalPositionKeyword), } +/// The computed value for an `image-set()` image. +pub type ImageSet = generic::GenericImageSet; + +impl ToComputedValue for specified::ImageSet { + type ComputedValue = 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(); + let mut selected_index = 0; + let mut selected_resolution = items[0].resolution.dppx(); + for (i, item) in items.iter().enumerate().skip(1) { + let candidate_resolution = item.resolution.dppx(); + + // https://drafts.csswg.org/css-images-4/#image-set-notation: + // + // Make a UA-specific choice of which to load, based on whatever + // criteria deemed relevant (such as the resolution of the + // display, connection speed, etc). + // + // For now, select the lowest resolution greater than display + // density, otherwise the greatest resolution available + let better_candidate = || { + if selected_resolution < dpr && candidate_resolution > selected_resolution { + return true; + } + if candidate_resolution < selected_resolution && candidate_resolution >= dpr { + return true; + } + false + }; + + if better_candidate() { + selected_index = i; + selected_resolution = candidate_resolution; + } + } + + ImageSet { + selected_index, + items, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + Self { + selected_index: 0, + items: ToComputedValue::from_computed_value(&computed.items), + } + } +} + + /// Computed values for `-moz-image-rect(...)`. #[cfg(feature = "gecko")] pub type MozImageRect = generic::GenericMozImageRect; /// Empty enum on non-gecko #[cfg(not(feature = "gecko"))] -pub type MozImageRect = crate::values::specified::image::MozImageRect; +pub type MozImageRect = specified::MozImageRect; impl generic::LineDirection for LineDirection { fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool { @@ -116,28 +169,28 @@ impl generic::LineDirection for LineDirection { } } -impl ToComputedValue for SpecifiedLineDirection { +impl ToComputedValue for specified::LineDirection { type ComputedValue = LineDirection; fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { - SpecifiedLineDirection::Angle(ref angle) => { + specified::LineDirection::Angle(ref angle) => { LineDirection::Angle(angle.to_computed_value(context)) }, - SpecifiedLineDirection::Horizontal(x) => LineDirection::Horizontal(x), - SpecifiedLineDirection::Vertical(y) => LineDirection::Vertical(y), - SpecifiedLineDirection::Corner(x, y) => LineDirection::Corner(x, y), + specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x), + specified::LineDirection::Vertical(y) => LineDirection::Vertical(y), + specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y), } } fn from_computed_value(computed: &Self::ComputedValue) -> Self { match *computed { LineDirection::Angle(ref angle) => { - SpecifiedLineDirection::Angle(ToComputedValue::from_computed_value(angle)) + specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle)) }, - LineDirection::Horizontal(x) => SpecifiedLineDirection::Horizontal(x), - LineDirection::Vertical(y) => SpecifiedLineDirection::Vertical(y), - LineDirection::Corner(x, y) => SpecifiedLineDirection::Corner(x, y), + LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x), + LineDirection::Vertical(y) => specified::LineDirection::Vertical(y), + LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y), } } } diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index ff6498d668a..b4301cd1b29 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -53,7 +53,7 @@ pub enum GenericImage CrossFade(Box>), /// An `image-set()` function. - ImageSet(Box>), + ImageSet(#[compute(field_bound)] Box>), } pub use self::GenericImage as Image; @@ -141,10 +141,14 @@ 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, + Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, )] #[repr(C)] pub struct GenericImageSet { + /// The index of the selected candidate. Zero for specified values. + #[css(skip)] + pub selected_index: usize, + /// All of the image and resolution pairs. #[css(iterable)] pub items: crate::OwnedSlice>, diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 1a0711231b3..b8cfeb1bbbb 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -324,6 +324,7 @@ impl ImageSet { input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode)) })?; Ok(Self { + selected_index: 0, items: items.into() }) }