1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! CSS handling for the computed value of 6 //! [`image`][image]s 7 //! 8 //! [image]: https://drafts.csswg.org/css-images/#image-values 9 10 use crate::values::computed::percentage::Percentage; 11 use crate::values::computed::position::Position; 12 use crate::values::computed::url::ComputedImageUrl; 13 #[cfg(feature = "gecko")] 14 use crate::values::computed::NumberOrPercentage; 15 use crate::values::computed::{Angle, Color, Context}; 16 use crate::values::computed::{ 17 AngleOrPercentage, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, 18 Resolution, ToComputedValue, 19 }; 20 use crate::values::generics::image::{self as generic, GradientCompatMode}; 21 use crate::values::specified::image as specified; 22 use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword}; 23 use std::f32::consts::PI; 24 use std::fmt::{self, Write}; 25 use style_traits::{CssWriter, ToCss}; 26 27 /// Computed values for an image according to CSS-IMAGES. 28 /// <https://drafts.csswg.org/css-images/#image-values> 29 pub type Image = 30 generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl, Color, Percentage, Resolution>; 31 32 /// Computed values for a CSS gradient. 33 /// <https://drafts.csswg.org/css-images/#gradients> 34 pub type Gradient = generic::GenericGradient< 35 LineDirection, 36 LengthPercentage, 37 NonNegativeLength, 38 NonNegativeLengthPercentage, 39 Position, 40 Angle, 41 AngleOrPercentage, 42 Color, 43 >; 44 45 /// Computed values for CSS cross-fade 46 /// <https://drafts.csswg.org/css-images-4/#cross-fade-function> 47 pub type CrossFade = generic::CrossFade<Image, Color, Percentage>; 48 /// A computed percentage or nothing. 49 pub type PercentOrNone = generic::PercentOrNone<Percentage>; 50 51 /// A computed radial gradient ending shape. 52 pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>; 53 54 /// A computed gradient line direction. 55 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)] 56 #[repr(C, u8)] 57 pub enum LineDirection { 58 /// An angle. 59 Angle(Angle), 60 /// A horizontal direction. 61 Horizontal(HorizontalPositionKeyword), 62 /// A vertical direction. 63 Vertical(VerticalPositionKeyword), 64 /// A corner. 65 Corner(HorizontalPositionKeyword, VerticalPositionKeyword), 66 } 67 68 /// The computed value for an `image-set()` image. 69 pub type ImageSet = generic::GenericImageSet<Image, Resolution>; 70 71 impl ToComputedValue for specified::ImageSet { 72 type ComputedValue = ImageSet; 73 to_computed_value(&self, context: &Context) -> Self::ComputedValue74 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 75 let items = self.items.to_computed_value(context); 76 let dpr = context.device().device_pixel_ratio().get(); 77 78 // If no item have a supported MIME type, the behavior is undefined by the standard 79 // By default, we select the first item 80 let mut supported_image = false; 81 let mut selected_index = 0; 82 let mut selected_resolution = items[0].resolution.dppx(); 83 84 for (i, item) in items.iter().enumerate() { 85 86 // If the MIME type is not supported, we discard the ImageSetItem 87 if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) { 88 continue; 89 } 90 91 let candidate_resolution = item.resolution.dppx(); 92 93 // https://drafts.csswg.org/css-images-4/#image-set-notation: 94 // 95 // Make a UA-specific choice of which to load, based on whatever 96 // criteria deemed relevant (such as the resolution of the 97 // display, connection speed, etc). 98 // 99 // For now, select the lowest resolution greater than display 100 // density, otherwise the greatest resolution available 101 let better_candidate = || { 102 if selected_resolution < dpr && candidate_resolution > selected_resolution { 103 return true; 104 } 105 if candidate_resolution < selected_resolution && candidate_resolution >= dpr { 106 return true; 107 } 108 false 109 }; 110 111 // The first item with a supported MIME type is obviously the current best candidate 112 if !supported_image || better_candidate() { 113 supported_image = true; 114 selected_index = i; 115 selected_resolution = candidate_resolution; 116 } 117 } 118 119 ImageSet { 120 selected_index, 121 items, 122 } 123 } 124 from_computed_value(computed: &Self::ComputedValue) -> Self125 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 126 Self { 127 selected_index: 0, 128 items: ToComputedValue::from_computed_value(&computed.items), 129 } 130 } 131 } 132 133 /// Computed values for `-moz-image-rect(...)`. 134 #[cfg(feature = "gecko")] 135 pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, ComputedImageUrl>; 136 137 /// Empty enum on non-gecko 138 #[cfg(not(feature = "gecko"))] 139 pub type MozImageRect = specified::MozImageRect; 140 141 impl generic::LineDirection for LineDirection { points_downwards(&self, compat_mode: GradientCompatMode) -> bool142 fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool { 143 match *self { 144 LineDirection::Angle(angle) => angle.radians() == PI, 145 LineDirection::Vertical(VerticalPositionKeyword::Bottom) => { 146 compat_mode == GradientCompatMode::Modern 147 }, 148 LineDirection::Vertical(VerticalPositionKeyword::Top) => { 149 compat_mode != GradientCompatMode::Modern 150 }, 151 _ => false, 152 } 153 } 154 to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result where W: Write,155 fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result 156 where 157 W: Write, 158 { 159 match *self { 160 LineDirection::Angle(ref angle) => angle.to_css(dest), 161 LineDirection::Horizontal(x) => { 162 if compat_mode == GradientCompatMode::Modern { 163 dest.write_str("to ")?; 164 } 165 x.to_css(dest) 166 }, 167 LineDirection::Vertical(y) => { 168 if compat_mode == GradientCompatMode::Modern { 169 dest.write_str("to ")?; 170 } 171 y.to_css(dest) 172 }, 173 LineDirection::Corner(x, y) => { 174 if compat_mode == GradientCompatMode::Modern { 175 dest.write_str("to ")?; 176 } 177 x.to_css(dest)?; 178 dest.write_str(" ")?; 179 y.to_css(dest) 180 }, 181 } 182 } 183 } 184 185 impl ToComputedValue for specified::LineDirection { 186 type ComputedValue = LineDirection; 187 to_computed_value(&self, context: &Context) -> Self::ComputedValue188 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 189 match *self { 190 specified::LineDirection::Angle(ref angle) => { 191 LineDirection::Angle(angle.to_computed_value(context)) 192 }, 193 specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x), 194 specified::LineDirection::Vertical(y) => LineDirection::Vertical(y), 195 specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y), 196 } 197 } 198 from_computed_value(computed: &Self::ComputedValue) -> Self199 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 200 match *computed { 201 LineDirection::Angle(ref angle) => { 202 specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle)) 203 }, 204 LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x), 205 LineDirection::Vertical(y) => specified::LineDirection::Vertical(y), 206 LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y), 207 } 208 } 209 } 210