1use crate::Zero;
8use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10use crate::typed_om::{ToTyped, TypedValue};
11use crate::values::computed::border::BorderSideWidth as ComputedBorderSideWidth;
12use crate::values::computed::{Context, ToComputedValue};
13use crate::values::generics::border::{
14 GenericBorderCornerRadius, GenericBorderImageSideWidth, GenericBorderImageSlice,
15 GenericBorderRadius, GenericBorderSpacing,
16};
17use crate::values::generics::rect::Rect;
18use crate::values::generics::size::Size2D;
19use crate::values::specified::length::{Length, NonNegativeLength, NonNegativeLengthPercentage};
20use crate::values::specified::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage};
21use app_units::Au;
22use cssparser::Parser;
23use std::fmt::{self, Write};
24use style_traits::{CssWriter, ParseError, ToCss};
25use thin_vec::ThinVec;
26
27#[allow(missing_docs)]
32#[derive(
33 Clone,
34 Copy,
35 Debug,
36 Deserialize,
37 Eq,
38 FromPrimitive,
39 MallocSizeOf,
40 Ord,
41 Parse,
42 PartialEq,
43 PartialOrd,
44 Serialize,
45 SpecifiedValueInfo,
46 ToComputedValue,
47 ToCss,
48 ToResolvedValue,
49 ToShmem,
50 ToTyped,
51)]
52#[repr(u8)]
53pub enum BorderStyle {
54 Hidden,
55 None,
56 Inset,
57 Groove,
58 Outset,
59 Ridge,
60 Dotted,
61 Dashed,
62 Solid,
63 Double,
64}
65
66impl BorderStyle {
67 #[inline]
69 pub fn none_or_hidden(&self) -> bool {
70 matches!(*self, BorderStyle::None | BorderStyle::Hidden)
71 }
72}
73
74pub type BorderImageWidth = Rect<BorderImageSideWidth>;
76
77impl ToTyped for BorderImageWidth {
78 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
83 if !self.all_sides_equal() {
84 return Err(());
85 }
86
87 self.0.to_typed(dest)
88 }
89}
90
91pub type BorderImageSideWidth =
93 GenericBorderImageSideWidth<NonNegativeLengthPercentage, NonNegativeNumber>;
94
95pub type BorderImageSlice = GenericBorderImageSlice<NonNegativeNumberOrPercentage>;
97
98impl ToTyped for BorderImageSlice {
99 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
104 if self.fill {
105 return Err(());
106 }
107
108 let offsets = &self.offsets;
109
110 if !offsets.all_sides_equal() {
111 return Err(());
112 }
113
114 offsets.0.to_typed(dest)
115 }
116}
117
118pub type BorderRadius = GenericBorderRadius<NonNegativeLengthPercentage>;
120
121pub type BorderCornerRadius = GenericBorderCornerRadius<NonNegativeLengthPercentage>;
123
124pub type BorderSpacing = GenericBorderSpacing<NonNegativeLength>;
126
127impl BorderImageSlice {
128 #[inline]
130 pub fn hundred_percent() -> Self {
131 GenericBorderImageSlice {
132 offsets: Rect::all(NonNegativeNumberOrPercentage::hundred_percent()),
133 fill: false,
134 }
135 }
136}
137
138#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
140pub enum LineWidth {
141 Thin,
143 Medium,
145 Thick,
147 Length(NonNegativeLength),
149}
150
151impl LineWidth {
152 #[inline]
154 pub fn zero() -> Self {
155 Self::Length(NonNegativeLength::zero())
156 }
157
158 fn parse_quirky(
159 context: &ParserContext,
160 input: &mut Parser,
161 allow_quirks: AllowQuirks,
162 ) -> Result<Self, ParseError> {
163 if let Ok(length) =
164 input.try_parse(|i| NonNegativeLength::parse_quirky(context, i, allow_quirks))
165 {
166 return Ok(Self::Length(length));
167 }
168 Ok(try_match_ident_ignore_ascii_case! { input,
169 "thin" => Self::Thin,
170 "medium" => Self::Medium,
171 "thick" => Self::Thick,
172 })
173 }
174}
175
176impl Parse for LineWidth {
177 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
178 Self::parse_quirky(context, input, AllowQuirks::No)
179 }
180}
181
182impl ToComputedValue for LineWidth {
183 type ComputedValue = Au;
184
185 #[inline]
186 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
187 match *self {
188 Self::Thin => Au::from_px(1),
190 Self::Medium => Au::from_px(3),
191 Self::Thick => Au::from_px(5),
192 Self::Length(ref length) => Au::from_f32_px(length.to_computed_value(context).px()),
193 }
194 }
195
196 #[inline]
197 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
198 Self::Length(NonNegativeLength::from_px(computed.to_f32_px()))
199 }
200}
201
202#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
205pub struct BorderSideWidth(LineWidth);
206
207impl BorderSideWidth {
208 pub fn medium() -> Self {
210 Self(LineWidth::Medium)
211 }
212
213 pub fn from_px(px: f32) -> Self {
215 Self(LineWidth::Length(Length::from_px(px).into()))
216 }
217
218 pub fn parse_quirky(
220 context: &ParserContext,
221 input: &mut Parser,
222 allow_quirks: AllowQuirks,
223 ) -> Result<Self, ParseError> {
224 Ok(Self(LineWidth::parse_quirky(context, input, allow_quirks)?))
225 }
226}
227
228impl Parse for BorderSideWidth {
229 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
230 Self::parse_quirky(context, input, AllowQuirks::No)
231 }
232}
233
234fn snap_as_border_width(len: Au, context: &Context) -> Au {
236 debug_assert!(len >= Au(0));
237
238 if len == Au(0) {
241 return len;
242 }
243
244 let au_per_dev_px = context.device().app_units_per_device_pixel();
245 std::cmp::max(Au(au_per_dev_px), Au(len.0 / au_per_dev_px * au_per_dev_px))
246}
247
248impl ToComputedValue for BorderSideWidth {
249 type ComputedValue = ComputedBorderSideWidth;
250
251 #[inline]
252 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
253 ComputedBorderSideWidth(snap_as_border_width(
254 self.0.to_computed_value(context),
255 context,
256 ))
257 }
258
259 #[inline]
260 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
261 Self(LineWidth::from_computed_value(&computed.0))
262 }
263}
264
265#[derive(
267 Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
268)]
269pub struct BorderSideOffset(Length);
270
271impl ToComputedValue for BorderSideOffset {
272 type ComputedValue = Au;
273
274 #[inline]
275 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
276 let offset = Au::from_f32_px(self.0.to_computed_value(context).px());
277 let should_snap = match crate::pref!("layout.css.outline-offset.snapping") {
278 1 => true,
279 2 => context.device().chrome_rules_enabled_for_document(),
280 _ => false,
281 };
282 if !should_snap {
283 return offset;
284 }
285 if offset < Au(0) {
286 -snap_as_border_width(-offset, context)
287 } else {
288 snap_as_border_width(offset, context)
289 }
290 }
291
292 #[inline]
293 fn from_computed_value(computed: &Au) -> Self {
294 Self(Length::from_px(computed.to_f32_px()))
295 }
296}
297
298impl BorderImageSideWidth {
299 #[inline]
301 pub fn one() -> Self {
302 GenericBorderImageSideWidth::Number(NonNegativeNumber::new(1.))
303 }
304}
305
306impl Parse for BorderImageSlice {
307 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
308 let mut fill = input.try_parse(|i| i.expect_ident_matching("fill")).is_ok();
309 let offsets = Rect::parse_with(context, input, NonNegativeNumberOrPercentage::parse)?;
310 if !fill {
311 fill = input.try_parse(|i| i.expect_ident_matching("fill")).is_ok();
312 }
313 Ok(GenericBorderImageSlice { offsets, fill })
314 }
315}
316
317impl Parse for BorderRadius {
318 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
319 let widths = Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?;
320 let heights = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
321 Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?
322 } else {
323 widths.clone()
324 };
325
326 Ok(GenericBorderRadius {
327 top_left: BorderCornerRadius::new(widths.0, heights.0),
328 top_right: BorderCornerRadius::new(widths.1, heights.1),
329 bottom_right: BorderCornerRadius::new(widths.2, heights.2),
330 bottom_left: BorderCornerRadius::new(widths.3, heights.3),
331 })
332 }
333}
334
335impl Parse for BorderCornerRadius {
336 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
337 Size2D::parse_with(context, input, NonNegativeLengthPercentage::parse)
338 .map(GenericBorderCornerRadius)
339 }
340}
341
342impl Parse for BorderSpacing {
343 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
344 Size2D::parse_with(context, input, |context, input| {
345 NonNegativeLength::parse_quirky(context, input, AllowQuirks::Yes)
346 })
347 .map(GenericBorderSpacing)
348 }
349}
350
351#[allow(missing_docs)]
353#[derive(
354 Clone,
355 Copy,
356 Debug,
357 Deserialize,
358 Eq,
359 MallocSizeOf,
360 Parse,
361 PartialEq,
362 Serialize,
363 SpecifiedValueInfo,
364 ToComputedValue,
365 ToCss,
366 ToResolvedValue,
367 ToShmem,
368)]
369#[repr(u8)]
370pub enum BorderImageRepeatKeyword {
371 Stretch,
372 Repeat,
373 Round,
374 Space,
375}
376
377#[derive(
381 Clone,
382 Copy,
383 Debug,
384 MallocSizeOf,
385 PartialEq,
386 SpecifiedValueInfo,
387 ToComputedValue,
388 ToResolvedValue,
389 ToShmem,
390 ToTyped,
391)]
392#[repr(C)]
393#[typed(todo_derive_fields)]
394pub struct BorderImageRepeat(pub BorderImageRepeatKeyword, pub BorderImageRepeatKeyword);
395
396impl ToCss for BorderImageRepeat {
397 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
398 where
399 W: Write,
400 {
401 self.0.to_css(dest)?;
402 if self.0 != self.1 {
403 dest.write_char(' ')?;
404 self.1.to_css(dest)?;
405 }
406 Ok(())
407 }
408}
409
410impl BorderImageRepeat {
411 #[inline]
413 pub fn stretch() -> Self {
414 BorderImageRepeat(
415 BorderImageRepeatKeyword::Stretch,
416 BorderImageRepeatKeyword::Stretch,
417 )
418 }
419}
420
421impl Parse for BorderImageRepeat {
422 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
423 let horizontal = BorderImageRepeatKeyword::parse(input)?;
424 let vertical = input.try_parse(BorderImageRepeatKeyword::parse).ok();
425 Ok(BorderImageRepeat(
426 horizontal,
427 vertical.unwrap_or(horizontal),
428 ))
429 }
430}
431
432#[allow(missing_docs)]
434#[derive(
435 Clone,
436 Copy,
437 Debug,
438 Deserialize,
439 Eq,
440 FromPrimitive,
441 Hash,
442 MallocSizeOf,
443 Parse,
444 PartialEq,
445 Serialize,
446 SpecifiedValueInfo,
447 ToComputedValue,
448 ToCss,
449 ToResolvedValue,
450 ToShmem,
451 ToTyped,
452)]
453#[repr(u8)]
454pub enum BoxDecorationBreak {
455 Slice,
456 Clone,
457}
458
459#[allow(missing_docs)]
461#[derive(
462 Clone,
463 Copy,
464 Debug,
465 Deserialize,
466 Eq,
467 FromPrimitive,
468 Hash,
469 MallocSizeOf,
470 Parse,
471 PartialEq,
472 Serialize,
473 SpecifiedValueInfo,
474 ToComputedValue,
475 ToCss,
476 ToResolvedValue,
477 ToShmem,
478 ToTyped,
479)]
480#[repr(u8)]
481pub enum FloatEdge {
482 ContentBox,
483 MarginBox,
484}