tor-browser

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

color.rs (8065B)


      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 //! Generic types for color properties.
      6 
      7 use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorFunction};
      8 use crate::derives::*;
      9 use crate::values::{
     10    computed::ToComputedValue, specified::percentage::ToPercentage, ParseError, Parser,
     11 };
     12 use std::fmt::{self, Write};
     13 use style_traits::{CssWriter, ToCss};
     14 
     15 /// This struct represents a combined color from a numeric color and
     16 /// the current foreground color (currentcolor keyword).
     17 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem, ToTyped)]
     18 #[repr(C)]
     19 pub enum GenericColor<Percentage> {
     20    /// The actual numeric color.
     21    Absolute(AbsoluteColor),
     22    /// A unresolvable color.
     23    ColorFunction(Box<ColorFunction<Self>>),
     24    /// The `CurrentColor` keyword.
     25    CurrentColor,
     26    /// The color-mix() function.
     27    ColorMix(Box<GenericColorMix<Self, Percentage>>),
     28    /// The contrast-color() function.
     29    ContrastColor(Box<Self>),
     30 }
     31 
     32 /// Flags used to modify the calculation of a color mix result.
     33 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
     34 #[repr(C)]
     35 pub struct ColorMixFlags(u8);
     36 bitflags! {
     37    impl ColorMixFlags : u8 {
     38        /// Normalize the weights of the mix.
     39        const NORMALIZE_WEIGHTS = 1 << 0;
     40        /// The result should always be converted to the modern color syntax.
     41        const RESULT_IN_MODERN_SYNTAX = 1 << 1;
     42    }
     43 }
     44 
     45 /// A restricted version of the css `color-mix()` function, which only supports
     46 /// percentages.
     47 ///
     48 /// https://drafts.csswg.org/css-color-5/#color-mix
     49 #[derive(
     50    Clone,
     51    Debug,
     52    MallocSizeOf,
     53    PartialEq,
     54    ToAnimatedValue,
     55    ToComputedValue,
     56    ToResolvedValue,
     57    ToShmem,
     58 )]
     59 #[allow(missing_docs)]
     60 #[repr(C)]
     61 pub struct GenericColorMix<Color, Percentage> {
     62    pub interpolation: ColorInterpolationMethod,
     63    pub left: Color,
     64    pub left_percentage: Percentage,
     65    pub right: Color,
     66    pub right_percentage: Percentage,
     67    pub flags: ColorMixFlags,
     68 }
     69 
     70 pub use self::GenericColorMix as ColorMix;
     71 
     72 impl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, Percentage> {
     73    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     74    where
     75        W: Write,
     76    {
     77        fn can_omit<Percentage: ToPercentage>(
     78            percent: &Percentage,
     79            other: &Percentage,
     80            is_left: bool,
     81        ) -> bool {
     82            if percent.is_calc() {
     83                return false;
     84            }
     85            if percent.to_percentage() == 0.5 {
     86                return other.to_percentage() == 0.5;
     87            }
     88            if is_left {
     89                return false;
     90            }
     91            (1.0 - percent.to_percentage() - other.to_percentage()).abs() <= f32::EPSILON
     92        }
     93 
     94        dest.write_str("color-mix(")?;
     95 
     96        // If the color interpolation method is oklab (which is now the default),
     97        // it can be omitted.
     98        // See: https://github.com/web-platform-tests/interop/issues/1166
     99        if !self.interpolation.is_default() {
    100            self.interpolation.to_css(dest)?;
    101            dest.write_str(", ")?;
    102        }
    103 
    104        self.left.to_css(dest)?;
    105        if !can_omit(&self.left_percentage, &self.right_percentage, true) {
    106            dest.write_char(' ')?;
    107            self.left_percentage.to_css(dest)?;
    108        }
    109        dest.write_str(", ")?;
    110        self.right.to_css(dest)?;
    111        if !can_omit(&self.right_percentage, &self.left_percentage, false) {
    112            dest.write_char(' ')?;
    113            self.right_percentage.to_css(dest)?;
    114        }
    115        dest.write_char(')')
    116    }
    117 }
    118 
    119 impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
    120    /// Mix the colors so that we get a single color. If any of the 2 colors are
    121    /// not mixable (perhaps not absolute?), then return None.
    122    pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>
    123    where
    124        Percentage: ToPercentage,
    125    {
    126        let left = self.left.as_absolute()?;
    127        let right = self.right.as_absolute()?;
    128 
    129        Some(crate::color::mix::mix(
    130            self.interpolation,
    131            &left,
    132            self.left_percentage.to_percentage(),
    133            &right,
    134            self.right_percentage.to_percentage(),
    135            self.flags,
    136        ))
    137    }
    138 }
    139 
    140 pub use self::GenericColor as Color;
    141 
    142 impl<Percentage> Color<Percentage> {
    143    /// If this color is absolute return it's value, otherwise return None.
    144    pub fn as_absolute(&self) -> Option<&AbsoluteColor> {
    145        match *self {
    146            Self::Absolute(ref absolute) => Some(absolute),
    147            _ => None,
    148        }
    149    }
    150 
    151    /// Returns a color value representing currentcolor.
    152    pub fn currentcolor() -> Self {
    153        Self::CurrentColor
    154    }
    155 
    156    /// Whether it is a currentcolor value (no numeric color component).
    157    pub fn is_currentcolor(&self) -> bool {
    158        matches!(*self, Self::CurrentColor)
    159    }
    160 
    161    /// Whether this color is an absolute color.
    162    pub fn is_absolute(&self) -> bool {
    163        matches!(*self, Self::Absolute(..))
    164    }
    165 }
    166 
    167 /// Either `<color>` or `auto`.
    168 #[derive(
    169    Animate,
    170    Clone,
    171    ComputeSquaredDistance,
    172    Copy,
    173    Debug,
    174    MallocSizeOf,
    175    PartialEq,
    176    Parse,
    177    SpecifiedValueInfo,
    178    ToAnimatedValue,
    179    ToAnimatedZero,
    180    ToComputedValue,
    181    ToResolvedValue,
    182    ToCss,
    183    ToShmem,
    184    ToTyped,
    185 )]
    186 #[repr(C, u8)]
    187 pub enum GenericColorOrAuto<C> {
    188    /// A `<color>`.
    189    Color(C),
    190    /// `auto`
    191    Auto,
    192 }
    193 
    194 pub use self::GenericColorOrAuto as ColorOrAuto;
    195 
    196 /// Caret color is effectively a ColorOrAuto, but resolves `auto` to
    197 /// currentColor.
    198 #[derive(
    199    Animate,
    200    Clone,
    201    ComputeSquaredDistance,
    202    Copy,
    203    Debug,
    204    MallocSizeOf,
    205    PartialEq,
    206    SpecifiedValueInfo,
    207    ToAnimatedValue,
    208    ToAnimatedZero,
    209    ToComputedValue,
    210    ToCss,
    211    ToShmem,
    212    ToTyped,
    213 )]
    214 #[repr(transparent)]
    215 pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
    216 
    217 impl<C> GenericCaretColor<C> {
    218    /// Returns the `auto` value.
    219    pub fn auto() -> Self {
    220        GenericCaretColor(GenericColorOrAuto::Auto)
    221    }
    222 }
    223 
    224 pub use self::GenericCaretColor as CaretColor;
    225 
    226 /// A light-dark(<light>, <dark>) function.
    227 #[derive(
    228    Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue,
    229 )]
    230 #[css(function = "light-dark", comma)]
    231 #[repr(C)]
    232 pub struct GenericLightDark<T> {
    233    /// The value returned when using a light theme.
    234    pub light: T,
    235    /// The value returned when using a dark theme.
    236    pub dark: T,
    237 }
    238 
    239 impl<T> GenericLightDark<T> {
    240    /// Parse the arguments of the light-dark() function.
    241    pub fn parse_args_with<'i>(
    242        input: &mut Parser<'i, '_>,
    243        mut parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
    244    ) -> Result<Self, ParseError<'i>> {
    245        let light = parse_one(input)?;
    246        input.expect_comma()?;
    247        let dark = parse_one(input)?;
    248        Ok(Self { light, dark })
    249    }
    250 
    251    /// Parse the light-dark() function.
    252    pub fn parse_with<'i>(
    253        input: &mut Parser<'i, '_>,
    254        parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
    255    ) -> Result<Self, ParseError<'i>> {
    256        input.expect_function_matching("light-dark")?;
    257        input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))
    258    }
    259 }
    260 
    261 impl<T: ToComputedValue> GenericLightDark<T> {
    262    /// Choose the light or dark version of this value for computation purposes, and compute it.
    263    pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {
    264        let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);
    265        if cx.for_non_inherited_property {
    266            cx.rule_cache_conditions
    267                .borrow_mut()
    268                .set_color_scheme_dependency(cx.builder.color_scheme);
    269        }
    270        let chosen = if dark { &self.dark } else { &self.light };
    271        chosen.to_computed_value(cx)
    272    }
    273 }