tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

image.rs (9695B)


      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::derives::*;
     11 use crate::values::computed::percentage::Percentage;
     12 use crate::values::computed::position::Position;
     13 use crate::values::computed::url::ComputedUrl;
     14 use crate::values::computed::{Angle, Color, Context};
     15 use crate::values::computed::{
     16    AngleOrPercentage, Length, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
     17    Resolution, ToComputedValue,
     18 };
     19 use crate::values::generics::image::{self as generic, GradientCompatMode};
     20 use crate::values::specified::image as specified;
     21 use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword};
     22 use std::f32::consts::PI;
     23 use std::fmt::{self, Write};
     24 use style_traits::{CssWriter, ToCss};
     25 
     26 pub use specified::ImageRendering;
     27 
     28 /// Computed values for an image according to CSS-IMAGES.
     29 /// <https://drafts.csswg.org/css-images/#image-values>
     30 pub type Image = generic::GenericImage<Gradient, ComputedUrl, Color, Percentage, Resolution>;
     31 
     32 // Images should remain small, see https://github.com/servo/servo/pull/18430
     33 #[cfg(feature = "gecko")]
     34 size_of_test!(Image, 16);
     35 #[cfg(feature = "servo")]
     36 size_of_test!(Image, 24);
     37 
     38 /// Computed values for a CSS gradient.
     39 /// <https://drafts.csswg.org/css-images/#gradients>
     40 pub type Gradient = generic::GenericGradient<
     41    LineDirection,
     42    Length,
     43    LengthPercentage,
     44    Position,
     45    Angle,
     46    AngleOrPercentage,
     47    Color,
     48 >;
     49 
     50 /// Computed values for CSS cross-fade
     51 /// <https://drafts.csswg.org/css-images-4/#cross-fade-function>
     52 pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
     53 
     54 /// A computed radial gradient ending shape.
     55 pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>;
     56 
     57 /// A computed gradient line direction.
     58 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
     59 #[repr(C, u8)]
     60 pub enum LineDirection {
     61    /// An angle.
     62    Angle(Angle),
     63    /// A horizontal direction.
     64    Horizontal(HorizontalPositionKeyword),
     65    /// A vertical direction.
     66    Vertical(VerticalPositionKeyword),
     67    /// A corner.
     68    Corner(HorizontalPositionKeyword, VerticalPositionKeyword),
     69 }
     70 
     71 /// The computed value for an `image-set()` image.
     72 pub type ImageSet = generic::GenericImageSet<Image, Resolution>;
     73 
     74 impl ToComputedValue for specified::ImageSet {
     75    type ComputedValue = ImageSet;
     76 
     77    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
     78        let items = self.items.to_computed_value(context);
     79        let dpr = context.device().device_pixel_ratio().get();
     80 
     81        let mut supported_image = false;
     82        let mut selected_index = std::usize::MAX;
     83        let mut selected_resolution = 0.0;
     84 
     85        for (i, item) in items.iter().enumerate() {
     86            if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) {
     87                // If the MIME type is not supported, we discard the ImageSetItem.
     88                continue;
     89            }
     90 
     91            let candidate_resolution = item.resolution.dppx();
     92            debug_assert!(
     93                candidate_resolution >= 0.0,
     94                "Resolutions should be non-negative"
     95            );
     96            if candidate_resolution == 0.0 {
     97                // If the resolution is 0, we also treat it as an invalid image.
     98                continue;
     99            }
    100 
    101            // https://drafts.csswg.org/css-images-4/#image-set-notation:
    102            //
    103            //     Make a UA-specific choice of which to load, based on whatever criteria deemed
    104            //     relevant (such as the resolution of the display, connection speed, etc).
    105            //
    106            // For now, select the lowest resolution greater than display density, otherwise the
    107            // greatest resolution available.
    108            let better_candidate = || {
    109                if selected_resolution < dpr && candidate_resolution > selected_resolution {
    110                    return true;
    111                }
    112                if candidate_resolution < selected_resolution && candidate_resolution >= dpr {
    113                    return true;
    114                }
    115                false
    116            };
    117 
    118            // The first item with a supported MIME type is obviously the current best candidate
    119            if !supported_image || better_candidate() {
    120                supported_image = true;
    121                selected_index = i;
    122                selected_resolution = candidate_resolution;
    123            }
    124        }
    125 
    126        ImageSet {
    127            selected_index,
    128            items,
    129        }
    130    }
    131 
    132    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
    133        Self {
    134            selected_index: std::usize::MAX,
    135            items: ToComputedValue::from_computed_value(&computed.items),
    136        }
    137    }
    138 }
    139 
    140 impl generic::LineDirection for LineDirection {
    141    fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool {
    142        match *self {
    143            LineDirection::Angle(angle) => angle.radians() == PI,
    144            LineDirection::Vertical(VerticalPositionKeyword::Bottom) => {
    145                compat_mode == GradientCompatMode::Modern
    146            },
    147            LineDirection::Vertical(VerticalPositionKeyword::Top) => {
    148                compat_mode != GradientCompatMode::Modern
    149            },
    150            _ => false,
    151        }
    152    }
    153 
    154    fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
    155    where
    156        W: Write,
    157    {
    158        match *self {
    159            LineDirection::Angle(ref angle) => angle.to_css(dest),
    160            LineDirection::Horizontal(x) => {
    161                if compat_mode == GradientCompatMode::Modern {
    162                    dest.write_str("to ")?;
    163                }
    164                x.to_css(dest)
    165            },
    166            LineDirection::Vertical(y) => {
    167                if compat_mode == GradientCompatMode::Modern {
    168                    dest.write_str("to ")?;
    169                }
    170                y.to_css(dest)
    171            },
    172            LineDirection::Corner(x, y) => {
    173                if compat_mode == GradientCompatMode::Modern {
    174                    dest.write_str("to ")?;
    175                }
    176                x.to_css(dest)?;
    177                dest.write_char(' ')?;
    178                y.to_css(dest)
    179            },
    180        }
    181    }
    182 }
    183 
    184 impl ToComputedValue for specified::LineDirection {
    185    type ComputedValue = LineDirection;
    186 
    187    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
    188        match *self {
    189            specified::LineDirection::Angle(ref angle) => {
    190                LineDirection::Angle(angle.to_computed_value(context))
    191            },
    192            specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x),
    193            specified::LineDirection::Vertical(y) => LineDirection::Vertical(y),
    194            specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y),
    195        }
    196    }
    197 
    198    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
    199        match *computed {
    200            LineDirection::Angle(ref angle) => {
    201                specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle))
    202            },
    203            LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x),
    204            LineDirection::Vertical(y) => specified::LineDirection::Vertical(y),
    205            LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y),
    206        }
    207    }
    208 }
    209 
    210 impl ToComputedValue for specified::Image {
    211    type ComputedValue = Image;
    212 
    213    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
    214        match self {
    215            Self::None => Image::None,
    216            Self::Url(u) => Image::Url(u.to_computed_value(context)),
    217            Self::Gradient(g) => Image::Gradient(g.to_computed_value(context)),
    218            #[cfg(feature = "gecko")]
    219            Self::Element(e) => Image::Element(e.to_computed_value(context)),
    220            #[cfg(feature = "gecko")]
    221            Self::MozSymbolicIcon(e) => Image::MozSymbolicIcon(e.to_computed_value(context)),
    222            #[cfg(feature = "servo")]
    223            Self::PaintWorklet(w) => Image::PaintWorklet(w.to_computed_value(context)),
    224            Self::CrossFade(f) => Image::CrossFade(f.to_computed_value(context)),
    225            Self::ImageSet(s) => Image::ImageSet(s.to_computed_value(context)),
    226            Self::LightDark(ld) => ld.compute(context),
    227        }
    228    }
    229 
    230    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
    231        match computed {
    232            Image::None => Self::None,
    233            Image::Url(u) => Self::Url(ToComputedValue::from_computed_value(u)),
    234            Image::Gradient(g) => Self::Gradient(ToComputedValue::from_computed_value(g)),
    235            #[cfg(feature = "gecko")]
    236            Image::Element(e) => Self::Element(ToComputedValue::from_computed_value(e)),
    237            #[cfg(feature = "gecko")]
    238            Image::MozSymbolicIcon(e) => {
    239                Self::MozSymbolicIcon(ToComputedValue::from_computed_value(e))
    240            },
    241            #[cfg(feature = "servo")]
    242            Image::PaintWorklet(w) => Self::PaintWorklet(ToComputedValue::from_computed_value(w)),
    243            Image::CrossFade(f) => Self::CrossFade(ToComputedValue::from_computed_value(f)),
    244            Image::ImageSet(s) => Self::ImageSet(ToComputedValue::from_computed_value(s)),
    245            Image::LightDark(_) => unreachable!("Shouldn't have computed image-set values"),
    246        }
    247    }
    248 }