tor-browser

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

border.rs (12919B)


      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 //! Specified types for CSS values related to borders.
      6 
      7 use crate::derives::*;
      8 use crate::parser::{Parse, ParserContext};
      9 use crate::values::computed::border::BorderSideWidth as ComputedBorderSideWidth;
     10 use crate::values::computed::{Context, ToComputedValue};
     11 use crate::values::generics::border::{
     12    GenericBorderCornerRadius, GenericBorderImageSideWidth, GenericBorderImageSlice,
     13    GenericBorderRadius, GenericBorderSpacing,
     14 };
     15 use crate::values::generics::rect::Rect;
     16 use crate::values::generics::size::Size2D;
     17 use crate::values::specified::length::{Length, NonNegativeLength, NonNegativeLengthPercentage};
     18 use crate::values::specified::Color;
     19 use crate::values::specified::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage};
     20 use crate::Zero;
     21 use app_units::Au;
     22 use cssparser::Parser;
     23 use std::fmt::{self, Write};
     24 use style_traits::{values::SequenceWriter, CssWriter, ParseError, ToCss};
     25 
     26 /// A specified value for a single side of a `border-style` property.
     27 ///
     28 /// The order here corresponds to the integer values from the border conflict
     29 /// resolution rules in CSS 2.1 ยง 17.6.2.1. Higher values override lower values.
     30 #[allow(missing_docs)]
     31 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
     32 #[derive(
     33    Clone,
     34    Copy,
     35    Debug,
     36    Eq,
     37    FromPrimitive,
     38    MallocSizeOf,
     39    Ord,
     40    Parse,
     41    PartialEq,
     42    PartialOrd,
     43    SpecifiedValueInfo,
     44    ToComputedValue,
     45    ToCss,
     46    ToResolvedValue,
     47    ToShmem,
     48    ToTyped,
     49 )]
     50 #[repr(u8)]
     51 pub enum BorderStyle {
     52    Hidden,
     53    None,
     54    Inset,
     55    Groove,
     56    Outset,
     57    Ridge,
     58    Dotted,
     59    Dashed,
     60    Solid,
     61    Double,
     62 }
     63 
     64 impl BorderStyle {
     65    /// Whether this border style is either none or hidden.
     66    #[inline]
     67    pub fn none_or_hidden(&self) -> bool {
     68        matches!(*self, BorderStyle::None | BorderStyle::Hidden)
     69    }
     70 }
     71 
     72 /// A specified value for the `border-image-width` property.
     73 pub type BorderImageWidth = Rect<BorderImageSideWidth>;
     74 
     75 /// A specified value for a single side of a `border-image-width` property.
     76 pub type BorderImageSideWidth =
     77    GenericBorderImageSideWidth<NonNegativeLengthPercentage, NonNegativeNumber>;
     78 
     79 /// A specified value for the `border-image-slice` property.
     80 pub type BorderImageSlice = GenericBorderImageSlice<NonNegativeNumberOrPercentage>;
     81 
     82 /// A specified value for the `border-radius` property.
     83 pub type BorderRadius = GenericBorderRadius<NonNegativeLengthPercentage>;
     84 
     85 /// A specified value for the `border-*-radius` longhand properties.
     86 pub type BorderCornerRadius = GenericBorderCornerRadius<NonNegativeLengthPercentage>;
     87 
     88 /// A specified value for the `border-spacing` longhand properties.
     89 pub type BorderSpacing = GenericBorderSpacing<NonNegativeLength>;
     90 
     91 impl BorderImageSlice {
     92    /// Returns the `100%` value.
     93    #[inline]
     94    pub fn hundred_percent() -> Self {
     95        GenericBorderImageSlice {
     96            offsets: Rect::all(NonNegativeNumberOrPercentage::hundred_percent()),
     97            fill: false,
     98        }
     99    }
    100 }
    101 
    102 /// https://drafts.csswg.org/css-backgrounds-3/#typedef-line-width
    103 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
    104 #[typed_value(derive_fields)]
    105 pub enum LineWidth {
    106    /// `thin`
    107    Thin,
    108    /// `medium`
    109    Medium,
    110    /// `thick`
    111    Thick,
    112    /// `<length>`
    113    Length(NonNegativeLength),
    114 }
    115 
    116 impl LineWidth {
    117    /// Returns the `0px` value.
    118    #[inline]
    119    pub fn zero() -> Self {
    120        Self::Length(NonNegativeLength::zero())
    121    }
    122 
    123    fn parse_quirky<'i, 't>(
    124        context: &ParserContext,
    125        input: &mut Parser<'i, 't>,
    126        allow_quirks: AllowQuirks,
    127    ) -> Result<Self, ParseError<'i>> {
    128        if let Ok(length) =
    129            input.try_parse(|i| NonNegativeLength::parse_quirky(context, i, allow_quirks))
    130        {
    131            return Ok(Self::Length(length));
    132        }
    133        Ok(try_match_ident_ignore_ascii_case! { input,
    134            "thin" => Self::Thin,
    135            "medium" => Self::Medium,
    136            "thick" => Self::Thick,
    137        })
    138    }
    139 }
    140 
    141 impl Parse for LineWidth {
    142    fn parse<'i>(
    143        context: &ParserContext,
    144        input: &mut Parser<'i, '_>,
    145    ) -> Result<Self, ParseError<'i>> {
    146        Self::parse_quirky(context, input, AllowQuirks::No)
    147    }
    148 }
    149 
    150 impl ToComputedValue for LineWidth {
    151    type ComputedValue = Au;
    152 
    153    #[inline]
    154    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
    155        match *self {
    156            // https://drafts.csswg.org/css-backgrounds-3/#line-width
    157            Self::Thin => Au::from_px(1),
    158            Self::Medium => Au::from_px(3),
    159            Self::Thick => Au::from_px(5),
    160            Self::Length(ref length) => Au::from_f32_px(length.to_computed_value(context).px()),
    161        }
    162    }
    163 
    164    #[inline]
    165    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
    166        Self::Length(NonNegativeLength::from_px(computed.to_f32_px()))
    167    }
    168 }
    169 
    170 /// A specified value for a single side of the `border-width` property. The difference between this
    171 /// and LineWidth is whether we snap to device pixels or not.
    172 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
    173 #[typed_value(derive_fields)]
    174 pub struct BorderSideWidth(LineWidth);
    175 
    176 impl BorderSideWidth {
    177    /// Returns the `medium` value.
    178    pub fn medium() -> Self {
    179        Self(LineWidth::Medium)
    180    }
    181 
    182    /// Returns a bare px value from the argument.
    183    pub fn from_px(px: f32) -> Self {
    184        Self(LineWidth::Length(Length::from_px(px).into()))
    185    }
    186 
    187    /// Parses, with quirks.
    188    pub fn parse_quirky<'i, 't>(
    189        context: &ParserContext,
    190        input: &mut Parser<'i, 't>,
    191        allow_quirks: AllowQuirks,
    192    ) -> Result<Self, ParseError<'i>> {
    193        Ok(Self(LineWidth::parse_quirky(context, input, allow_quirks)?))
    194    }
    195 }
    196 
    197 impl Parse for BorderSideWidth {
    198    fn parse<'i>(
    199        context: &ParserContext,
    200        input: &mut Parser<'i, '_>,
    201    ) -> Result<Self, ParseError<'i>> {
    202        Self::parse_quirky(context, input, AllowQuirks::No)
    203    }
    204 }
    205 
    206 // https://drafts.csswg.org/css-values-4/#snap-a-length-as-a-border-width
    207 fn snap_as_border_width(len: Au, context: &Context) -> Au {
    208    debug_assert!(len >= Au(0));
    209 
    210    // Round `width` down to the nearest device pixel, but any non-zero value that would round
    211    // down to zero is clamped to 1 device pixel.
    212    if len == Au(0) {
    213        return len;
    214    }
    215 
    216    let au_per_dev_px = context.device().app_units_per_device_pixel();
    217    std::cmp::max(Au(au_per_dev_px), Au(len.0 / au_per_dev_px * au_per_dev_px))
    218 }
    219 
    220 impl ToComputedValue for BorderSideWidth {
    221    type ComputedValue = ComputedBorderSideWidth;
    222 
    223    #[inline]
    224    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
    225        ComputedBorderSideWidth(snap_as_border_width(
    226            self.0.to_computed_value(context),
    227            context,
    228        ))
    229    }
    230 
    231    #[inline]
    232    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
    233        Self(LineWidth::from_computed_value(&computed.0))
    234    }
    235 }
    236 
    237 /// A specified value for outline-offset.
    238 #[derive(
    239    Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
    240 )]
    241 #[typed_value(derive_fields)]
    242 pub struct BorderSideOffset(Length);
    243 
    244 impl ToComputedValue for BorderSideOffset {
    245    type ComputedValue = Au;
    246 
    247    #[inline]
    248    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
    249        let offset = Au::from_f32_px(self.0.to_computed_value(context).px());
    250        let should_snap = match static_prefs::pref!("layout.css.outline-offset.snapping") {
    251            1 => true,
    252            2 => context.device().chrome_rules_enabled_for_document(),
    253            _ => false,
    254        };
    255        if !should_snap {
    256            return offset;
    257        }
    258        if offset < Au(0) {
    259            -snap_as_border_width(-offset, context)
    260        } else {
    261            snap_as_border_width(offset, context)
    262        }
    263    }
    264 
    265    #[inline]
    266    fn from_computed_value(computed: &Au) -> Self {
    267        Self(Length::from_px(computed.to_f32_px()))
    268    }
    269 }
    270 
    271 impl BorderImageSideWidth {
    272    /// Returns `1`.
    273    #[inline]
    274    pub fn one() -> Self {
    275        GenericBorderImageSideWidth::Number(NonNegativeNumber::new(1.))
    276    }
    277 }
    278 
    279 impl Parse for BorderImageSlice {
    280    fn parse<'i, 't>(
    281        context: &ParserContext,
    282        input: &mut Parser<'i, 't>,
    283    ) -> Result<Self, ParseError<'i>> {
    284        let mut fill = input.try_parse(|i| i.expect_ident_matching("fill")).is_ok();
    285        let offsets = Rect::parse_with(context, input, NonNegativeNumberOrPercentage::parse)?;
    286        if !fill {
    287            fill = input.try_parse(|i| i.expect_ident_matching("fill")).is_ok();
    288        }
    289        Ok(GenericBorderImageSlice { offsets, fill })
    290    }
    291 }
    292 
    293 impl Parse for BorderRadius {
    294    fn parse<'i, 't>(
    295        context: &ParserContext,
    296        input: &mut Parser<'i, 't>,
    297    ) -> Result<Self, ParseError<'i>> {
    298        let widths = Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?;
    299        let heights = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
    300            Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?
    301        } else {
    302            widths.clone()
    303        };
    304 
    305        Ok(GenericBorderRadius {
    306            top_left: BorderCornerRadius::new(widths.0, heights.0),
    307            top_right: BorderCornerRadius::new(widths.1, heights.1),
    308            bottom_right: BorderCornerRadius::new(widths.2, heights.2),
    309            bottom_left: BorderCornerRadius::new(widths.3, heights.3),
    310        })
    311    }
    312 }
    313 
    314 impl Parse for BorderCornerRadius {
    315    fn parse<'i, 't>(
    316        context: &ParserContext,
    317        input: &mut Parser<'i, 't>,
    318    ) -> Result<Self, ParseError<'i>> {
    319        Size2D::parse_with(context, input, NonNegativeLengthPercentage::parse)
    320            .map(GenericBorderCornerRadius)
    321    }
    322 }
    323 
    324 impl Parse for BorderSpacing {
    325    fn parse<'i, 't>(
    326        context: &ParserContext,
    327        input: &mut Parser<'i, 't>,
    328    ) -> Result<Self, ParseError<'i>> {
    329        Size2D::parse_with(context, input, |context, input| {
    330            NonNegativeLength::parse_quirky(context, input, AllowQuirks::Yes)
    331        })
    332        .map(GenericBorderSpacing)
    333    }
    334 }
    335 
    336 /// A single border-image-repeat keyword.
    337 #[allow(missing_docs)]
    338 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    339 #[derive(
    340    Clone,
    341    Copy,
    342    Debug,
    343    Eq,
    344    MallocSizeOf,
    345    Parse,
    346    PartialEq,
    347    SpecifiedValueInfo,
    348    ToComputedValue,
    349    ToCss,
    350    ToResolvedValue,
    351    ToShmem,
    352 )]
    353 #[repr(u8)]
    354 pub enum BorderImageRepeatKeyword {
    355    Stretch,
    356    Repeat,
    357    Round,
    358    Space,
    359 }
    360 
    361 /// The specified value for the `border-image-repeat` property.
    362 ///
    363 /// https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat
    364 #[derive(
    365    Clone,
    366    Copy,
    367    Debug,
    368    MallocSizeOf,
    369    PartialEq,
    370    SpecifiedValueInfo,
    371    ToComputedValue,
    372    ToResolvedValue,
    373    ToShmem,
    374    ToTyped,
    375 )]
    376 #[repr(C)]
    377 pub struct BorderImageRepeat(pub BorderImageRepeatKeyword, pub BorderImageRepeatKeyword);
    378 
    379 impl ToCss for BorderImageRepeat {
    380    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    381    where
    382        W: Write,
    383    {
    384        self.0.to_css(dest)?;
    385        if self.0 != self.1 {
    386            dest.write_char(' ')?;
    387            self.1.to_css(dest)?;
    388        }
    389        Ok(())
    390    }
    391 }
    392 
    393 impl BorderImageRepeat {
    394    /// Returns the `stretch` value.
    395    #[inline]
    396    pub fn stretch() -> Self {
    397        BorderImageRepeat(
    398            BorderImageRepeatKeyword::Stretch,
    399            BorderImageRepeatKeyword::Stretch,
    400        )
    401    }
    402 }
    403 
    404 impl Parse for BorderImageRepeat {
    405    fn parse<'i, 't>(
    406        _context: &ParserContext,
    407        input: &mut Parser<'i, 't>,
    408    ) -> Result<Self, ParseError<'i>> {
    409        let horizontal = BorderImageRepeatKeyword::parse(input)?;
    410        let vertical = input.try_parse(BorderImageRepeatKeyword::parse).ok();
    411        Ok(BorderImageRepeat(
    412            horizontal,
    413            vertical.unwrap_or(horizontal),
    414        ))
    415    }
    416 }
    417 
    418 /// Serializes a border shorthand value composed of width/style/color.
    419 pub fn serialize_directional_border<W>(
    420    dest: &mut CssWriter<W>,
    421    width: &BorderSideWidth,
    422    style: &BorderStyle,
    423    color: &Color,
    424 ) -> fmt::Result
    425 where
    426    W: Write,
    427 {
    428    let has_style = *style != BorderStyle::None;
    429    let has_color = *color != Color::CurrentColor;
    430    let has_width = *width != BorderSideWidth::medium();
    431    if !has_style && !has_color && !has_width {
    432        return width.to_css(dest);
    433    }
    434    let mut writer = SequenceWriter::new(dest, " ");
    435    if has_width {
    436        writer.item(width)?;
    437    }
    438    if has_style {
    439        writer.item(style)?;
    440    }
    441    if has_color {
    442        writer.item(color)?;
    443    }
    444    Ok(())
    445 }