tor-browser

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

to_css.rs (10626B)


      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 //! Write colors into CSS strings.
      6 
      7 use super::{
      8    parsing::{NumberOrAngleComponent, NumberOrPercentageComponent},
      9    AbsoluteColor, ColorFlags, ColorSpace,
     10 };
     11 use crate::values::normalize;
     12 use cssparser::color::{clamp_unit_f32, serialize_color_alpha, OPAQUE};
     13 use std::fmt::{self, Write};
     14 use style_traits::{CssWriter, ToCss};
     15 
     16 /// A [`ModernComponent`] can serialize to `none`, `nan`, `infinity` and
     17 /// floating point values.
     18 struct ModernComponent<'a>(&'a Option<f32>);
     19 
     20 impl<'a> ToCss for ModernComponent<'a> {
     21    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     22    where
     23        W: fmt::Write,
     24    {
     25        if let Some(value) = self.0 {
     26            if value.is_finite() {
     27                value.to_css(dest)
     28            } else if value.is_nan() {
     29                dest.write_str("calc(NaN)")
     30            } else {
     31                debug_assert!(value.is_infinite());
     32                if value.is_sign_negative() {
     33                    dest.write_str("calc(-infinity)")
     34                } else {
     35                    dest.write_str("calc(infinity)")
     36                }
     37            }
     38        } else {
     39            dest.write_str("none")
     40        }
     41    }
     42 }
     43 
     44 impl ToCss for NumberOrPercentageComponent {
     45    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     46    where
     47        W: Write,
     48    {
     49        use crate::values::computed::Percentage;
     50 
     51        match self {
     52            Self::Number(number) => number.to_css(dest)?,
     53            Self::Percentage(percentage) => Percentage(*percentage).to_css(dest)?,
     54        }
     55        Ok(())
     56    }
     57 }
     58 
     59 impl ToCss for NumberOrAngleComponent {
     60    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     61    where
     62        W: Write,
     63    {
     64        use crate::values::computed::Angle;
     65 
     66        match self {
     67            Self::Number(number) => number.to_css(dest)?,
     68            Self::Angle(degrees) => Angle::from_degrees(*degrees).to_css(dest)?,
     69        }
     70        Ok(())
     71    }
     72 }
     73 
     74 impl ToCss for AbsoluteColor {
     75    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     76    where
     77        W: Write,
     78    {
     79        match self.color_space {
     80            ColorSpace::Srgb if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) => {
     81                // The "none" keyword is not supported in the rgb/rgba legacy syntax.
     82                let has_alpha = self.alpha != OPAQUE;
     83 
     84                dest.write_str(if has_alpha { "rgba(" } else { "rgb(" })?;
     85                clamp_unit_f32(self.components.0).to_css(dest)?;
     86                dest.write_str(", ")?;
     87                clamp_unit_f32(self.components.1).to_css(dest)?;
     88                dest.write_str(", ")?;
     89                clamp_unit_f32(self.components.2).to_css(dest)?;
     90 
     91                // Legacy syntax does not allow none components.
     92                serialize_color_alpha(dest, Some(self.alpha), true)?;
     93 
     94                dest.write_char(')')
     95            },
     96            ColorSpace::Hsl | ColorSpace::Hwb => {
     97                if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) {
     98                    self.into_srgb_legacy().to_css(dest)
     99                } else {
    100                    self.to_color_space(ColorSpace::Srgb).to_css(dest)
    101                }
    102            },
    103            ColorSpace::Oklab | ColorSpace::Lab | ColorSpace::Oklch | ColorSpace::Lch => {
    104                if let ColorSpace::Oklab | ColorSpace::Oklch = self.color_space {
    105                    dest.write_str("ok")?;
    106                }
    107                if let ColorSpace::Oklab | ColorSpace::Lab = self.color_space {
    108                    dest.write_str("lab(")?;
    109                } else {
    110                    dest.write_str("lch(")?;
    111                }
    112                ModernComponent(&self.c0()).to_css(dest)?;
    113                dest.write_char(' ')?;
    114                ModernComponent(&self.c1()).to_css(dest)?;
    115                dest.write_char(' ')?;
    116                ModernComponent(&self.c2()).to_css(dest)?;
    117                serialize_color_alpha(dest, self.alpha(), false)?;
    118                dest.write_char(')')
    119            },
    120            _ => {
    121                #[cfg(debug_assertions)]
    122                match self.color_space {
    123                    ColorSpace::Srgb => {
    124                        debug_assert!(
    125                            !self.flags.contains(ColorFlags::IS_LEGACY_SRGB),
    126                            "legacy srgb is not a color function"
    127                        );
    128                    },
    129                    ColorSpace::SrgbLinear
    130                    | ColorSpace::DisplayP3
    131                    | ColorSpace::DisplayP3Linear
    132                    | ColorSpace::A98Rgb
    133                    | ColorSpace::ProphotoRgb
    134                    | ColorSpace::Rec2020
    135                    | ColorSpace::XyzD50
    136                    | ColorSpace::XyzD65 => {
    137                        // These color spaces are allowed.
    138                    },
    139                    ColorSpace::Hsl
    140                    | ColorSpace::Hwb
    141                    | ColorSpace::Lab
    142                    | ColorSpace::Oklab
    143                    | ColorSpace::Lch
    144                    | ColorSpace::Oklch => {
    145                        unreachable!("other color spaces do not support color() syntax")
    146                    },
    147                };
    148 
    149                dest.write_str("color(")?;
    150                self.color_space.to_css(dest)?;
    151                dest.write_char(' ')?;
    152                ModernComponent(&self.c0()).to_css(dest)?;
    153                dest.write_char(' ')?;
    154                ModernComponent(&self.c1()).to_css(dest)?;
    155                dest.write_char(' ')?;
    156                ModernComponent(&self.c2()).to_css(dest)?;
    157 
    158                serialize_color_alpha(dest, self.alpha(), false)?;
    159 
    160                dest.write_char(')')
    161            },
    162        }
    163    }
    164 }
    165 
    166 impl AbsoluteColor {
    167    /// Write a string to `dest` that represents a color as an author would
    168    /// enter it.
    169    /// NOTE: The format of the output is NOT according to any specification,
    170    /// but makes assumptions about the best ways that authors would want to
    171    /// enter color values in style sheets, devtools, etc.
    172    pub fn write_author_preferred_value<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    173    where
    174        W: Write,
    175    {
    176        macro_rules! precision {
    177            ($v:expr) => {{
    178                ($v * 100.0).round() / 100.0
    179            }};
    180        }
    181        macro_rules! number {
    182            ($c:expr) => {{
    183                if let Some(v) = $c.map(normalize) {
    184                    precision!(v).to_css(dest)?;
    185                } else {
    186                    write!(dest, "none")?;
    187                }
    188            }};
    189        }
    190        macro_rules! percentage {
    191            ($c:expr) => {{
    192                if let Some(v) = $c.map(normalize) {
    193                    precision!(v).to_css(dest)?;
    194                    dest.write_char('%')?;
    195                } else {
    196                    write!(dest, "none")?;
    197                }
    198            }};
    199        }
    200        macro_rules! unit_percentage {
    201            ($c:expr) => {{
    202                if let Some(v) = $c.map(normalize) {
    203                    precision!(v * 100.0).to_css(dest)?;
    204                    dest.write_char('%')?;
    205                } else {
    206                    write!(dest, "none")?;
    207                }
    208            }};
    209        }
    210        macro_rules! angle {
    211            ($c:expr) => {{
    212                if let Some(v) = $c.map(normalize) {
    213                    precision!(v).to_css(dest)?;
    214                    dest.write_str("deg")?;
    215                } else {
    216                    write!(dest, "none")?;
    217                }
    218            }};
    219        }
    220 
    221        match self.color_space {
    222            ColorSpace::Srgb => {
    223                write!(dest, "rgb(")?;
    224                unit_percentage!(self.c0());
    225                dest.write_char(' ')?;
    226                unit_percentage!(self.c1());
    227                dest.write_char(' ')?;
    228                unit_percentage!(self.c2());
    229                serialize_color_alpha(dest, self.alpha(), false)?;
    230                dest.write_char(')')
    231            },
    232            ColorSpace::Hsl | ColorSpace::Hwb => {
    233                dest.write_str(if self.color_space == ColorSpace::Hsl {
    234                    "hsl("
    235                } else {
    236                    "hwb("
    237                })?;
    238                angle!(self.c0());
    239                dest.write_char(' ')?;
    240                percentage!(self.c1());
    241                dest.write_char(' ')?;
    242                percentage!(self.c2());
    243                serialize_color_alpha(dest, self.alpha(), false)?;
    244                dest.write_char(')')
    245            },
    246            ColorSpace::Lab | ColorSpace::Oklab => {
    247                if self.color_space == ColorSpace::Oklab {
    248                    dest.write_str("ok")?;
    249                }
    250                dest.write_str("lab(")?;
    251                if self.color_space == ColorSpace::Lab {
    252                    percentage!(self.c0())
    253                } else {
    254                    unit_percentage!(self.c0())
    255                }
    256                dest.write_char(' ')?;
    257                number!(self.c1());
    258                dest.write_char(' ')?;
    259                number!(self.c2());
    260                serialize_color_alpha(dest, self.alpha(), false)?;
    261                dest.write_char(')')
    262            },
    263            ColorSpace::Lch | ColorSpace::Oklch => {
    264                if self.color_space == ColorSpace::Oklch {
    265                    dest.write_str("ok")?;
    266                }
    267                dest.write_str("lch(")?;
    268                number!(self.c0());
    269                dest.write_char(' ')?;
    270                number!(self.c1());
    271                dest.write_char(' ')?;
    272                angle!(self.c2());
    273                serialize_color_alpha(dest, self.alpha(), false)?;
    274                dest.write_char(')')
    275            },
    276            ColorSpace::SrgbLinear
    277            | ColorSpace::DisplayP3
    278            | ColorSpace::DisplayP3Linear
    279            | ColorSpace::A98Rgb
    280            | ColorSpace::ProphotoRgb
    281            | ColorSpace::Rec2020
    282            | ColorSpace::XyzD50
    283            | ColorSpace::XyzD65 => {
    284                dest.write_str("color(")?;
    285                self.color_space.to_css(dest)?;
    286                dest.write_char(' ')?;
    287                number!(self.c0());
    288                dest.write_char(' ')?;
    289                number!(self.c1());
    290                dest.write_char(' ')?;
    291                number!(self.c2());
    292                serialize_color_alpha(dest, self.alpha(), false)?;
    293                dest.write_char(')')
    294            },
    295        }
    296    }
    297 }