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::parser::{Parse, ParserContext};
8 use crate::values::computed::{self, Context, ToComputedValue};
9 use crate::values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
10 use crate::values::generics::border::BorderImageSideWidth as GenericBorderImageSideWidth;
11 use crate::values::generics::border::BorderImageSlice as GenericBorderImageSlice;
12 use crate::values::generics::border::BorderRadius as GenericBorderRadius;
13 use crate::values::generics::border::BorderSpacing as GenericBorderSpacing;
14 use crate::values::generics::rect::Rect;
15 use crate::values::generics::size::Size2D;
16 use crate::values::specified::length::{NonNegativeLength, NonNegativeLengthPercentage};
17 use crate::values::specified::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage};
18 use crate::Zero;
19 use cssparser::Parser;
20 use std::fmt::{self, Write};
21 use style_traits::{CssWriter, ParseError, ToCss};
22 
23 /// A specified value for a single side of a `border-style` property.
24 ///
25 /// The order here corresponds to the integer values from the border conflict
26 /// resolution rules in CSS 2.1 § 17.6.2.1. Higher values override lower values.
27 #[allow(missing_docs)]
28 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
29 #[derive(
30     Clone,
31     Copy,
32     Debug,
33     Eq,
34     FromPrimitive,
35     MallocSizeOf,
36     Ord,
37     Parse,
38     PartialEq,
39     PartialOrd,
40     SpecifiedValueInfo,
41     ToComputedValue,
42     ToCss,
43     ToResolvedValue,
44     ToShmem,
45 )]
46 #[repr(u8)]
47 pub enum BorderStyle {
48     Hidden,
49     None,
50     Inset,
51     Groove,
52     Outset,
53     Ridge,
54     Dotted,
55     Dashed,
56     Solid,
57     Double,
58 }
59 
60 impl BorderStyle {
61     /// Whether this border style is either none or hidden.
62     #[inline]
none_or_hidden(&self) -> bool63     pub fn none_or_hidden(&self) -> bool {
64         matches!(*self, BorderStyle::None | BorderStyle::Hidden)
65     }
66 }
67 
68 /// A specified value for a single side of the `border-width` property.
69 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
70 pub enum BorderSideWidth {
71     /// `thin`
72     Thin,
73     /// `medium`
74     Medium,
75     /// `thick`
76     Thick,
77     /// `<length>`
78     Length(NonNegativeLength),
79 }
80 
81 /// A specified value for the `border-image-width` property.
82 pub type BorderImageWidth = Rect<BorderImageSideWidth>;
83 
84 /// A specified value for a single side of a `border-image-width` property.
85 pub type BorderImageSideWidth =
86     GenericBorderImageSideWidth<NonNegativeLengthPercentage, NonNegativeNumber>;
87 
88 /// A specified value for the `border-image-slice` property.
89 pub type BorderImageSlice = GenericBorderImageSlice<NonNegativeNumberOrPercentage>;
90 
91 /// A specified value for the `border-radius` property.
92 pub type BorderRadius = GenericBorderRadius<NonNegativeLengthPercentage>;
93 
94 /// A specified value for the `border-*-radius` longhand properties.
95 pub type BorderCornerRadius = GenericBorderCornerRadius<NonNegativeLengthPercentage>;
96 
97 /// A specified value for the `border-spacing` longhand properties.
98 pub type BorderSpacing = GenericBorderSpacing<NonNegativeLength>;
99 
100 impl BorderImageSlice {
101     /// Returns the `100%` value.
102     #[inline]
hundred_percent() -> Self103     pub fn hundred_percent() -> Self {
104         GenericBorderImageSlice {
105             offsets: Rect::all(NonNegativeNumberOrPercentage::hundred_percent()),
106             fill: false,
107         }
108     }
109 }
110 
111 impl BorderSideWidth {
112     /// Returns the `0px` value.
113     #[inline]
zero() -> Self114     pub fn zero() -> Self {
115         BorderSideWidth::Length(NonNegativeLength::zero())
116     }
117 
118     /// Parses, with quirks.
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>119     pub fn parse_quirky<'i, 't>(
120         context: &ParserContext,
121         input: &mut Parser<'i, 't>,
122         allow_quirks: AllowQuirks,
123     ) -> Result<Self, ParseError<'i>> {
124         if let Ok(length) =
125             input.try_parse(|i| NonNegativeLength::parse_quirky(context, i, allow_quirks))
126         {
127             return Ok(BorderSideWidth::Length(length));
128         }
129         try_match_ident_ignore_ascii_case! { input,
130             "thin" => Ok(BorderSideWidth::Thin),
131             "medium" => Ok(BorderSideWidth::Medium),
132             "thick" => Ok(BorderSideWidth::Thick),
133         }
134     }
135 }
136 
137 impl Parse for BorderSideWidth {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>138     fn parse<'i, 't>(
139         context: &ParserContext,
140         input: &mut Parser<'i, 't>,
141     ) -> Result<Self, ParseError<'i>> {
142         Self::parse_quirky(context, input, AllowQuirks::No)
143     }
144 }
145 
146 impl ToComputedValue for BorderSideWidth {
147     type ComputedValue = computed::NonNegativeLength;
148 
149     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue150     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
151         // We choose the pixel length of the keyword values the same as both spec and gecko.
152         // Spec: https://drafts.csswg.org/css-backgrounds-3/#line-width
153         // Gecko: https://bugzilla.mozilla.org/show_bug.cgi?id=1312155#c0
154         match *self {
155             BorderSideWidth::Thin => NonNegativeLength::from_px(1.).to_computed_value(context),
156             BorderSideWidth::Medium => NonNegativeLength::from_px(3.).to_computed_value(context),
157             BorderSideWidth::Thick => NonNegativeLength::from_px(5.).to_computed_value(context),
158             BorderSideWidth::Length(ref length) => length.to_computed_value(context),
159         }
160         .into()
161     }
162 
163     #[inline]
from_computed_value(computed: &Self::ComputedValue) -> Self164     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
165         BorderSideWidth::Length(ToComputedValue::from_computed_value(computed))
166     }
167 }
168 
169 impl BorderImageSideWidth {
170     /// Returns `1`.
171     #[inline]
one() -> Self172     pub fn one() -> Self {
173         GenericBorderImageSideWidth::Number(NonNegativeNumber::new(1.))
174     }
175 }
176 
177 impl Parse for BorderImageSlice {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>178     fn parse<'i, 't>(
179         context: &ParserContext,
180         input: &mut Parser<'i, 't>,
181     ) -> Result<Self, ParseError<'i>> {
182         let mut fill = input.try_parse(|i| i.expect_ident_matching("fill")).is_ok();
183         let offsets = Rect::parse_with(context, input, NonNegativeNumberOrPercentage::parse)?;
184         if !fill {
185             fill = input.try_parse(|i| i.expect_ident_matching("fill")).is_ok();
186         }
187         Ok(GenericBorderImageSlice { offsets, fill })
188     }
189 }
190 
191 impl Parse for BorderRadius {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>192     fn parse<'i, 't>(
193         context: &ParserContext,
194         input: &mut Parser<'i, 't>,
195     ) -> Result<Self, ParseError<'i>> {
196         let widths = Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?;
197         let heights = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
198             Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?
199         } else {
200             widths.clone()
201         };
202 
203         Ok(GenericBorderRadius {
204             top_left: BorderCornerRadius::new(widths.0, heights.0),
205             top_right: BorderCornerRadius::new(widths.1, heights.1),
206             bottom_right: BorderCornerRadius::new(widths.2, heights.2),
207             bottom_left: BorderCornerRadius::new(widths.3, heights.3),
208         })
209     }
210 }
211 
212 impl Parse for BorderCornerRadius {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>213     fn parse<'i, 't>(
214         context: &ParserContext,
215         input: &mut Parser<'i, 't>,
216     ) -> Result<Self, ParseError<'i>> {
217         Size2D::parse_with(context, input, NonNegativeLengthPercentage::parse)
218             .map(GenericBorderCornerRadius)
219     }
220 }
221 
222 impl Parse for BorderSpacing {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>223     fn parse<'i, 't>(
224         context: &ParserContext,
225         input: &mut Parser<'i, 't>,
226     ) -> Result<Self, ParseError<'i>> {
227         Size2D::parse_with(context, input, |context, input| {
228             NonNegativeLength::parse_quirky(context, input, AllowQuirks::Yes)
229         })
230         .map(GenericBorderSpacing)
231     }
232 }
233 
234 /// A single border-image-repeat keyword.
235 #[allow(missing_docs)]
236 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
237 #[derive(
238     Clone,
239     Copy,
240     Debug,
241     Eq,
242     MallocSizeOf,
243     Parse,
244     PartialEq,
245     SpecifiedValueInfo,
246     ToComputedValue,
247     ToCss,
248     ToResolvedValue,
249     ToShmem,
250 )]
251 pub enum BorderImageRepeatKeyword {
252     Stretch,
253     Repeat,
254     Round,
255     Space,
256 }
257 
258 /// The specified value for the `border-image-repeat` property.
259 ///
260 /// https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat
261 #[derive(
262     Clone,
263     Copy,
264     Debug,
265     MallocSizeOf,
266     PartialEq,
267     SpecifiedValueInfo,
268     ToComputedValue,
269     ToResolvedValue,
270     ToShmem,
271 )]
272 pub struct BorderImageRepeat(pub BorderImageRepeatKeyword, pub BorderImageRepeatKeyword);
273 
274 impl ToCss for BorderImageRepeat {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,275     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
276     where
277         W: Write,
278     {
279         self.0.to_css(dest)?;
280         if self.0 != self.1 {
281             dest.write_str(" ")?;
282             self.1.to_css(dest)?;
283         }
284         Ok(())
285     }
286 }
287 
288 impl BorderImageRepeat {
289     /// Returns the `stretch` value.
290     #[inline]
stretch() -> Self291     pub fn stretch() -> Self {
292         BorderImageRepeat(
293             BorderImageRepeatKeyword::Stretch,
294             BorderImageRepeatKeyword::Stretch,
295         )
296     }
297 }
298 
299 impl Parse for BorderImageRepeat {
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>300     fn parse<'i, 't>(
301         _context: &ParserContext,
302         input: &mut Parser<'i, 't>,
303     ) -> Result<Self, ParseError<'i>> {
304         let horizontal = BorderImageRepeatKeyword::parse(input)?;
305         let vertical = input.try_parse(BorderImageRepeatKeyword::parse).ok();
306         Ok(BorderImageRepeat(
307             horizontal,
308             vertical.unwrap_or(horizontal),
309         ))
310     }
311 }
312