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