lightningcss/properties/
border.rs

1//! CSS properties related to borders.
2
3use super::border_image::*;
4use super::border_radius::*;
5use crate::compat::Feature;
6use crate::context::PropertyHandlerContext;
7use crate::declaration::{DeclarationBlock, DeclarationList};
8use crate::error::{ParserError, PrinterError};
9use crate::logical::PropertyCategory;
10use crate::macros::*;
11use crate::printer::Printer;
12use crate::properties::custom::UnparsedProperty;
13use crate::properties::{Property, PropertyId};
14use crate::targets::Browsers;
15use crate::targets::Targets;
16use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss};
17use crate::values::color::{ColorFallbackKind, CssColor};
18use crate::values::length::*;
19use crate::values::rect::Rect;
20use crate::values::size::Size2D;
21#[cfg(feature = "visitor")]
22use crate::visitor::Visit;
23use cssparser::*;
24
25/// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property.
26#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
27#[cfg_attr(feature = "visitor", derive(Visit))]
28#[cfg_attr(
29  feature = "serde",
30  derive(serde::Serialize, serde::Deserialize),
31  serde(tag = "type", content = "value", rename_all = "kebab-case")
32)]
33#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
34#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
35pub enum BorderSideWidth {
36  /// A UA defined `thin` value.
37  Thin,
38  /// A UA defined `medium` value.
39  Medium,
40  /// A UA defined `thick` value.
41  Thick,
42  /// An explicit width.
43  Length(Length),
44}
45
46impl Default for BorderSideWidth {
47  fn default() -> BorderSideWidth {
48    BorderSideWidth::Medium
49  }
50}
51
52impl IsCompatible for BorderSideWidth {
53  fn is_compatible(&self, browsers: Browsers) -> bool {
54    match self {
55      BorderSideWidth::Length(length) => length.is_compatible(browsers),
56      _ => true,
57    }
58  }
59}
60
61enum_property! {
62  /// A [`<line-style>`](https://drafts.csswg.org/css-backgrounds/#typedef-line-style) value, used in the `border-style` property.
63  pub enum LineStyle {
64    /// No border.
65    None,
66    /// Similar to `none` but with different rules for tables.
67    Hidden,
68    /// Looks as if the content on the inside of the border is sunken into the canvas.
69    Inset,
70    /// Looks as if it were carved in the canvas.
71    Groove,
72    /// Looks as if the content on the inside of the border is coming out of the canvas.
73    Outset,
74    /// Looks as if it were coming out of the canvas.
75    Ridge,
76    /// A series of round dots.
77    Dotted,
78    /// A series of square-ended dashes.
79    Dashed,
80    /// A single line segment.
81    Solid,
82    /// Two parallel solid lines with some space between them.
83    Double,
84  }
85}
86
87impl Default for LineStyle {
88  fn default() -> LineStyle {
89    LineStyle::None
90  }
91}
92
93impl IsCompatible for LineStyle {
94  fn is_compatible(&self, _browsers: Browsers) -> bool {
95    true
96  }
97}
98
99/// A generic type that represents the `border` and `outline` shorthand properties.
100#[derive(Debug, Clone, PartialEq)]
101#[cfg_attr(feature = "visitor", derive(Visit))]
102#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
103#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
104pub struct GenericBorder<S, const P: u8> {
105  /// The width of the border.
106  pub width: BorderSideWidth,
107  /// The border style.
108  pub style: S,
109  /// The border color.
110  pub color: CssColor,
111}
112
113#[cfg(feature = "into_owned")]
114impl<'any, S, const P: u8> static_self::IntoOwned<'any> for GenericBorder<S, P>
115where
116  S: static_self::IntoOwned<'any>,
117{
118  type Owned = GenericBorder<S::Owned, P>;
119  fn into_owned(self) -> Self::Owned {
120    GenericBorder {
121      width: self.width,
122      style: self.style.into_owned(),
123      color: self.color,
124    }
125  }
126}
127
128impl<S: Default, const P: u8> Default for GenericBorder<S, P> {
129  fn default() -> GenericBorder<S, P> {
130    GenericBorder {
131      width: BorderSideWidth::Medium,
132      style: S::default(),
133      color: CssColor::current_color(),
134    }
135  }
136}
137
138impl<'i, S: Parse<'i> + Default, const P: u8> Parse<'i> for GenericBorder<S, P> {
139  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
140    // Order doesn't matter...
141    let mut color = None;
142    let mut style = None;
143    let mut width = None;
144    let mut any = false;
145    loop {
146      if width.is_none() {
147        if let Ok(value) = input.try_parse(|i| BorderSideWidth::parse(i)) {
148          width = Some(value);
149          any = true;
150        }
151      }
152      if style.is_none() {
153        if let Ok(value) = input.try_parse(S::parse) {
154          style = Some(value);
155          any = true;
156          continue;
157        }
158      }
159      if color.is_none() {
160        if let Ok(value) = input.try_parse(|i| CssColor::parse(i)) {
161          color = Some(value);
162          any = true;
163          continue;
164        }
165      }
166      break;
167    }
168    if any {
169      Ok(GenericBorder {
170        width: width.unwrap_or(BorderSideWidth::Medium),
171        style: style.unwrap_or_default(),
172        color: color.unwrap_or_else(|| CssColor::current_color()),
173      })
174    } else {
175      Err(input.new_custom_error(ParserError::InvalidDeclaration))
176    }
177  }
178}
179
180impl<S: ToCss + Default + PartialEq, const P: u8> ToCss for GenericBorder<S, P> {
181  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
182  where
183    W: std::fmt::Write,
184  {
185    if *self == Self::default() {
186      self.style.to_css(dest)?;
187      return Ok(());
188    }
189
190    let mut needs_space = false;
191    if self.width != BorderSideWidth::default() {
192      self.width.to_css(dest)?;
193      needs_space = true;
194    }
195    if self.style != S::default() {
196      if needs_space {
197        dest.write_str(" ")?;
198      }
199      self.style.to_css(dest)?;
200      needs_space = true;
201    }
202    if self.color != CssColor::current_color() {
203      if needs_space {
204        dest.write_str(" ")?;
205      }
206      self.color.to_css(dest)?;
207    }
208    Ok(())
209  }
210}
211
212impl<S: Clone, const P: u8> FallbackValues for GenericBorder<S, P> {
213  fn get_fallbacks(&mut self, targets: Targets) -> Vec<Self> {
214    self
215      .color
216      .get_fallbacks(targets)
217      .into_iter()
218      .map(|color| GenericBorder {
219        color,
220        width: self.width.clone(),
221        style: self.style.clone(),
222      })
223      .collect()
224  }
225}
226
227/// A value for the [border-top](https://www.w3.org/TR/css-backgrounds-3/#propdef-border-top) shorthand property.
228pub type BorderTop = GenericBorder<LineStyle, 0>;
229/// A value for the [border-right](https://www.w3.org/TR/css-backgrounds-3/#propdef-border-right) shorthand property.
230pub type BorderRight = GenericBorder<LineStyle, 1>;
231/// A value for the [border-bottom](https://www.w3.org/TR/css-backgrounds-3/#propdef-border-bottom) shorthand property.
232pub type BorderBottom = GenericBorder<LineStyle, 2>;
233/// A value for the [border-left](https://www.w3.org/TR/css-backgrounds-3/#propdef-border-left) shorthand property.
234pub type BorderLeft = GenericBorder<LineStyle, 3>;
235/// A value for the [border-block-start](https://drafts.csswg.org/css-logical/#propdef-border-block-start) shorthand property.
236pub type BorderBlockStart = GenericBorder<LineStyle, 4>;
237/// A value for the [border-block-end](https://drafts.csswg.org/css-logical/#propdef-border-block-end) shorthand property.
238pub type BorderBlockEnd = GenericBorder<LineStyle, 5>;
239/// A value for the [border-inline-start](https://drafts.csswg.org/css-logical/#propdef-border-inline-start) shorthand property.
240pub type BorderInlineStart = GenericBorder<LineStyle, 6>;
241/// A value for the [border-inline-end](https://drafts.csswg.org/css-logical/#propdef-border-inline-end) shorthand property.
242pub type BorderInlineEnd = GenericBorder<LineStyle, 7>;
243/// A value for the [border-block](https://drafts.csswg.org/css-logical/#propdef-border-block) shorthand property.
244pub type BorderBlock = GenericBorder<LineStyle, 8>;
245/// A value for the [border-inline](https://drafts.csswg.org/css-logical/#propdef-border-inline) shorthand property.
246pub type BorderInline = GenericBorder<LineStyle, 9>;
247/// A value for the [border](https://www.w3.org/TR/css-backgrounds-3/#propdef-border) shorthand property.
248pub type Border = GenericBorder<LineStyle, 10>;
249
250impl_shorthand! {
251  BorderTop(BorderTop) {
252    width: [BorderTopWidth],
253    style: [BorderTopStyle],
254    color: [BorderTopColor],
255  }
256}
257
258impl_shorthand! {
259  BorderRight(BorderRight) {
260    width: [BorderRightWidth],
261    style: [BorderRightStyle],
262    color: [BorderRightColor],
263  }
264}
265
266impl_shorthand! {
267  BorderBottom(BorderBottom) {
268    width: [BorderBottomWidth],
269    style: [BorderBottomStyle],
270    color: [BorderBottomColor],
271  }
272}
273
274impl_shorthand! {
275  BorderLeft(BorderLeft) {
276    width: [BorderLeftWidth],
277    style: [BorderLeftStyle],
278    color: [BorderLeftColor],
279  }
280}
281
282impl_shorthand! {
283  BorderBlockStart(BorderBlockStart) {
284    width: [BorderBlockStartWidth],
285    style: [BorderBlockStartStyle],
286    color: [BorderBlockStartColor],
287  }
288}
289
290impl_shorthand! {
291  BorderBlockEnd(BorderBlockEnd) {
292    width: [BorderBlockEndWidth],
293    style: [BorderBlockEndStyle],
294    color: [BorderBlockEndColor],
295  }
296}
297
298impl_shorthand! {
299  BorderInlineStart(BorderInlineStart) {
300    width: [BorderInlineStartWidth],
301    style: [BorderInlineStartStyle],
302    color: [BorderInlineStartColor],
303  }
304}
305
306impl_shorthand! {
307  BorderInlineEnd(BorderInlineEnd) {
308    width: [BorderInlineEndWidth],
309    style: [BorderInlineEndStyle],
310    color: [BorderInlineEndColor],
311  }
312}
313
314impl_shorthand! {
315  BorderBlock(BorderBlock) {
316    width: [BorderBlockStartWidth, BorderBlockEndWidth],
317    style: [BorderBlockStartStyle, BorderBlockEndStyle],
318    color: [BorderBlockStartColor, BorderBlockEndColor],
319  }
320}
321
322impl_shorthand! {
323  BorderInline(BorderInline) {
324    width: [BorderInlineStartWidth, BorderInlineEndWidth],
325    style: [BorderInlineStartStyle, BorderInlineEndStyle],
326    color: [BorderInlineStartColor, BorderInlineEndColor],
327  }
328}
329
330impl_shorthand! {
331  Border(Border) {
332    width: [BorderTopWidth, BorderRightWidth, BorderBottomWidth, BorderLeftWidth],
333    style: [BorderTopStyle, BorderRightStyle, BorderBottomStyle, BorderLeftStyle],
334    color: [BorderTopColor, BorderRightColor, BorderBottomColor, BorderLeftColor],
335  }
336}
337
338size_shorthand! {
339  /// A value for the [border-block-color](https://drafts.csswg.org/css-logical/#propdef-border-block-color) shorthand property.
340  pub struct BorderBlockColor<CssColor> {
341    /// The block start value.
342    start: BorderBlockStartColor,
343    /// The block end value.
344    end: BorderBlockEndColor,
345  }
346}
347
348size_shorthand! {
349  /// A value for the [border-block-style](https://drafts.csswg.org/css-logical/#propdef-border-block-style) shorthand property.
350  pub struct BorderBlockStyle<LineStyle> {
351    /// The block start value.
352    start: BorderBlockStartStyle,
353    /// The block end value.
354    end: BorderBlockEndStyle,
355  }
356}
357
358size_shorthand! {
359  /// A value for the [border-block-width](https://drafts.csswg.org/css-logical/#propdef-border-block-width) shorthand property.
360  pub struct BorderBlockWidth<BorderSideWidth> {
361    /// The block start value.
362    start: BorderBlockStartWidth,
363    /// The block end value.
364    end: BorderBlockEndWidth,
365  }
366}
367
368size_shorthand! {
369  /// A value for the [border-inline-color](https://drafts.csswg.org/css-logical/#propdef-border-inline-color) shorthand property.
370  pub struct BorderInlineColor<CssColor> {
371    /// The inline start value.
372    start: BorderInlineStartColor,
373    /// The inline end value.
374    end: BorderInlineEndColor,
375  }
376}
377
378size_shorthand! {
379  /// A value for the [border-inline-style](https://drafts.csswg.org/css-logical/#propdef-border-inline-style) shorthand property.
380  pub struct BorderInlineStyle<LineStyle> {
381    /// The inline start value.
382    start: BorderInlineStartStyle,
383    /// The inline end value.
384    end: BorderInlineEndStyle,
385  }
386}
387
388size_shorthand! {
389  /// A value for the [border-inline-width](https://drafts.csswg.org/css-logical/#propdef-border-inline-width) shorthand property.
390  pub struct BorderInlineWidth<BorderSideWidth> {
391    /// The inline start value.
392    start: BorderInlineStartWidth,
393    /// The inline end value.
394    end: BorderInlineEndWidth,
395  }
396}
397
398rect_shorthand! {
399  /// A value for the [border-color](https://drafts.csswg.org/css-backgrounds/#propdef-border-color) shorthand property.
400  pub struct BorderColor<CssColor> {
401    BorderTopColor,
402    BorderRightColor,
403    BorderBottomColor,
404    BorderLeftColor
405  }
406}
407
408rect_shorthand! {
409  /// A value for the [border-style](https://drafts.csswg.org/css-backgrounds/#propdef-border-style) shorthand property.
410  pub struct BorderStyle<LineStyle> {
411    BorderTopStyle,
412    BorderRightStyle,
413    BorderBottomStyle,
414    BorderLeftStyle
415  }
416}
417
418rect_shorthand! {
419  /// A value for the [border-width](https://drafts.csswg.org/css-backgrounds/#propdef-border-width) shorthand property.
420  pub struct BorderWidth<BorderSideWidth> {
421    BorderTopWidth,
422    BorderRightWidth,
423    BorderBottomWidth,
424    BorderLeftWidth
425  }
426}
427
428macro_rules! impl_fallbacks {
429  ($t: ident $(, $name: ident)+) => {
430    impl FallbackValues for $t {
431      fn get_fallbacks(&mut self, targets: Targets) -> Vec<Self> {
432        let mut fallbacks = ColorFallbackKind::empty();
433        $(
434          fallbacks |= self.$name.get_necessary_fallbacks(targets);
435        )+
436
437        let mut res = Vec::new();
438        if fallbacks.contains(ColorFallbackKind::RGB) {
439          res.push($t {
440            $(
441              $name: self.$name.get_fallback(ColorFallbackKind::RGB),
442            )+
443          });
444        }
445
446        if fallbacks.contains(ColorFallbackKind::P3) {
447          res.push($t {
448            $(
449              $name: self.$name.get_fallback(ColorFallbackKind::P3),
450            )+
451          });
452        }
453
454        if fallbacks.contains(ColorFallbackKind::LAB) {
455          $(
456            self.$name = self.$name.get_fallback(ColorFallbackKind::LAB);
457          )+
458        }
459
460        res
461      }
462    }
463  }
464}
465
466impl_fallbacks!(BorderBlockColor, start, end);
467impl_fallbacks!(BorderInlineColor, start, end);
468impl_fallbacks!(BorderColor, top, right, bottom, left);
469
470#[derive(Default, Debug, PartialEq)]
471struct BorderShorthand {
472  pub width: Option<BorderSideWidth>,
473  pub style: Option<LineStyle>,
474  pub color: Option<CssColor>,
475}
476
477impl BorderShorthand {
478  pub fn set_border<const P: u8>(&mut self, border: &GenericBorder<LineStyle, P>) {
479    self.width = Some(border.width.clone());
480    self.style = Some(border.style.clone());
481    self.color = Some(border.color.clone());
482  }
483
484  pub fn is_valid(&self) -> bool {
485    self.width.is_some() && self.style.is_some() && self.color.is_some()
486  }
487
488  pub fn reset(&mut self) {
489    self.width = None;
490    self.style = None;
491    self.color = None;
492  }
493
494  pub fn to_border<const P: u8>(&self) -> GenericBorder<LineStyle, P> {
495    GenericBorder {
496      width: self.width.clone().unwrap(),
497      style: self.style.clone().unwrap(),
498      color: self.color.clone().unwrap(),
499    }
500  }
501}
502
503property_bitflags! {
504  #[derive(Debug, Default)]
505  struct BorderProperty: u32 {
506    const BorderTopColor = 1 << 0;
507    const BorderBottomColor = 1 << 1;
508    const BorderLeftColor = 1 << 2;
509    const BorderRightColor = 1 << 3;
510    const BorderBlockStartColor = 1 << 4;
511    const BorderBlockEndColor = 1 << 5;
512    const BorderBlockColor = Self::BorderBlockStartColor.bits() | Self::BorderBlockEndColor.bits();
513    const BorderInlineStartColor = 1 << 6;
514    const BorderInlineEndColor = 1 << 7;
515    const BorderInlineColor = Self::BorderInlineStartColor.bits() | Self::BorderInlineEndColor.bits();
516    const BorderTopWidth = 1 << 8;
517    const BorderBottomWidth = 1 << 9;
518    const BorderLeftWidth = 1 << 10;
519    const BorderRightWidth = 1 << 11;
520    const BorderBlockStartWidth = 1 << 12;
521    const BorderBlockEndWidth = 1 << 13;
522    const BorderBlockWidth = Self::BorderBlockStartWidth.bits() | Self::BorderBlockEndWidth.bits();
523    const BorderInlineStartWidth = 1 << 14;
524    const BorderInlineEndWidth = 1 << 15;
525    const BorderInlineWidth = Self::BorderInlineStartWidth.bits() | Self::BorderInlineEndWidth.bits();
526    const BorderTopStyle = 1 << 16;
527    const BorderBottomStyle = 1 << 17;
528    const BorderLeftStyle = 1 << 18;
529    const BorderRightStyle = 1 << 19;
530    const BorderBlockStartStyle = 1 << 20;
531    const BorderBlockEndStyle = 1 << 21;
532    const BorderBlockStyle = Self::BorderBlockStartStyle.bits() | Self::BorderBlockEndStyle.bits();
533    const BorderInlineStartStyle = 1 << 22;
534    const BorderInlineEndStyle = 1 << 23;
535    const BorderInlineStyle = Self::BorderInlineStartStyle.bits() | Self::BorderInlineEndStyle.bits();
536    const BorderTop = Self::BorderTopColor.bits() | Self::BorderTopWidth.bits() | Self::BorderTopStyle.bits();
537    const BorderBottom = Self::BorderBottomColor.bits() | Self::BorderBottomWidth.bits() | Self::BorderBottomStyle.bits();
538    const BorderLeft = Self::BorderLeftColor.bits() | Self::BorderLeftWidth.bits() | Self::BorderLeftStyle.bits();
539    const BorderRight = Self::BorderRightColor.bits() | Self::BorderRightWidth.bits() | Self::BorderRightStyle.bits();
540    const BorderBlockStart = Self::BorderBlockStartColor.bits() | Self::BorderBlockStartWidth.bits() | Self::BorderBlockStartStyle.bits();
541    const BorderBlockEnd = Self::BorderBlockEndColor.bits() | Self::BorderBlockEndWidth.bits() | Self::BorderBlockEndStyle.bits();
542    const BorderInlineStart = Self::BorderInlineStartColor.bits() | Self::BorderInlineStartWidth.bits() | Self::BorderInlineStartStyle.bits();
543    const BorderInlineEnd = Self::BorderInlineEndColor.bits() | Self::BorderInlineEndWidth.bits() | Self::BorderInlineEndStyle.bits();
544    const BorderBlock = Self::BorderBlockStart.bits() | Self::BorderBlockEnd.bits();
545    const BorderInline = Self::BorderInlineStart.bits() | Self::BorderInlineEnd.bits();
546    const BorderWidth = Self::BorderLeftWidth.bits() | Self::BorderRightWidth.bits() | Self::BorderTopWidth.bits() | Self::BorderBottomWidth.bits();
547    const BorderStyle = Self::BorderLeftStyle.bits() | Self::BorderRightStyle.bits() | Self::BorderTopStyle.bits() | Self::BorderBottomStyle.bits();
548    const BorderColor = Self::BorderLeftColor.bits() | Self::BorderRightColor.bits() | Self::BorderTopColor.bits() | Self::BorderBottomColor.bits();
549    const Border = Self::BorderWidth.bits() | Self::BorderStyle.bits() | Self::BorderColor.bits();
550  }
551}
552
553#[derive(Debug, Default)]
554pub(crate) struct BorderHandler<'i> {
555  border_top: BorderShorthand,
556  border_bottom: BorderShorthand,
557  border_left: BorderShorthand,
558  border_right: BorderShorthand,
559  border_block_start: BorderShorthand,
560  border_block_end: BorderShorthand,
561  border_inline_start: BorderShorthand,
562  border_inline_end: BorderShorthand,
563  category: PropertyCategory,
564  border_image_handler: BorderImageHandler<'i>,
565  border_radius_handler: BorderRadiusHandler<'i>,
566  flushed_properties: BorderProperty,
567  has_any: bool,
568}
569
570impl<'i> PropertyHandler<'i> for BorderHandler<'i> {
571  fn handle_property(
572    &mut self,
573    property: &Property<'i>,
574    dest: &mut DeclarationList<'i>,
575    context: &mut PropertyHandlerContext<'i, '_>,
576  ) -> bool {
577    use Property::*;
578
579    macro_rules! flush {
580      ($key: ident, $prop: ident, $val: expr, $category: ident) => {{
581        if PropertyCategory::$category != self.category {
582          self.flush(dest, context);
583        }
584
585        if self.$key.$prop.is_some() && self.$key.$prop.as_ref().unwrap() != $val && matches!(context.targets.browsers, Some(targets) if !$val.is_compatible(targets)) {
586          self.flush(dest, context);
587        }
588      }};
589    }
590
591    macro_rules! property {
592      ($key: ident, $prop: ident, $val: expr, $category: ident) => {{
593        flush!($key, $prop, $val, $category);
594        self.$key.$prop = Some($val.clone());
595        self.category = PropertyCategory::$category;
596        self.has_any = true;
597      }};
598    }
599
600    macro_rules! set_border {
601      ($key: ident, $val: ident, $category: ident) => {{
602        if PropertyCategory::$category != self.category {
603          self.flush(dest, context);
604        }
605        self.$key.set_border($val);
606        self.category = PropertyCategory::$category;
607        self.has_any = true;
608      }};
609    }
610
611    match &property {
612      BorderTopColor(val) => property!(border_top, color, val, Physical),
613      BorderBottomColor(val) => property!(border_bottom, color, val, Physical),
614      BorderLeftColor(val) => property!(border_left, color, val, Physical),
615      BorderRightColor(val) => property!(border_right, color, val, Physical),
616      BorderBlockStartColor(val) => property!(border_block_start, color, val, Logical),
617      BorderBlockEndColor(val) => property!(border_block_end, color, val, Logical),
618      BorderBlockColor(val) => {
619        property!(border_block_start, color, &val.start, Logical);
620        property!(border_block_end, color, &val.end, Logical);
621      }
622      BorderInlineStartColor(val) => property!(border_inline_start, color, val, Logical),
623      BorderInlineEndColor(val) => property!(border_inline_end, color, val, Logical),
624      BorderInlineColor(val) => {
625        property!(border_inline_start, color, &val.start, Logical);
626        property!(border_inline_end, color, &val.end, Logical);
627      }
628      BorderTopWidth(val) => property!(border_top, width, val, Physical),
629      BorderBottomWidth(val) => property!(border_bottom, width, val, Physical),
630      BorderLeftWidth(val) => property!(border_left, width, val, Physical),
631      BorderRightWidth(val) => property!(border_right, width, val, Physical),
632      BorderBlockStartWidth(val) => property!(border_block_start, width, val, Logical),
633      BorderBlockEndWidth(val) => property!(border_block_end, width, val, Logical),
634      BorderBlockWidth(val) => {
635        property!(border_block_start, width, &val.start, Logical);
636        property!(border_block_end, width, &val.end, Logical);
637      }
638      BorderInlineStartWidth(val) => property!(border_inline_start, width, val, Logical),
639      BorderInlineEndWidth(val) => property!(border_inline_end, width, val, Logical),
640      BorderInlineWidth(val) => {
641        property!(border_inline_start, width, &val.start, Logical);
642        property!(border_inline_end, width, &val.end, Logical);
643      }
644      BorderTopStyle(val) => property!(border_top, style, val, Physical),
645      BorderBottomStyle(val) => property!(border_bottom, style, val, Physical),
646      BorderLeftStyle(val) => property!(border_left, style, val, Physical),
647      BorderRightStyle(val) => property!(border_right, style, val, Physical),
648      BorderBlockStartStyle(val) => property!(border_block_start, style, val, Logical),
649      BorderBlockEndStyle(val) => property!(border_block_end, style, val, Logical),
650      BorderBlockStyle(val) => {
651        property!(border_block_start, style, &val.start, Logical);
652        property!(border_block_end, style, &val.end, Logical);
653      }
654      BorderInlineStartStyle(val) => property!(border_inline_start, style, val, Logical),
655      BorderInlineEndStyle(val) => property!(border_inline_end, style, val, Logical),
656      BorderInlineStyle(val) => {
657        property!(border_inline_start, style, &val.start, Logical);
658        property!(border_inline_end, style, &val.end, Logical);
659      }
660      BorderTop(val) => set_border!(border_top, val, Physical),
661      BorderBottom(val) => set_border!(border_bottom, val, Physical),
662      BorderLeft(val) => set_border!(border_left, val, Physical),
663      BorderRight(val) => set_border!(border_right, val, Physical),
664      BorderBlockStart(val) => set_border!(border_block_start, val, Logical),
665      BorderBlockEnd(val) => set_border!(border_block_end, val, Logical),
666      BorderInlineStart(val) => set_border!(border_inline_start, val, Logical),
667      BorderInlineEnd(val) => set_border!(border_inline_end, val, Logical),
668      BorderBlock(val) => {
669        set_border!(border_block_start, val, Logical);
670        set_border!(border_block_end, val, Logical);
671      }
672      BorderInline(val) => {
673        set_border!(border_inline_start, val, Logical);
674        set_border!(border_inline_end, val, Logical);
675      }
676      BorderWidth(val) => {
677        property!(border_top, width, &val.top, Physical);
678        property!(border_right, width, &val.right, Physical);
679        property!(border_bottom, width, &val.bottom, Physical);
680        property!(border_left, width, &val.left, Physical);
681        self.border_block_start.width = None;
682        self.border_block_end.width = None;
683        self.border_inline_start.width = None;
684        self.border_inline_end.width = None;
685        self.has_any = true;
686      }
687      BorderStyle(val) => {
688        property!(border_top, style, &val.top, Physical);
689        property!(border_right, style, &val.right, Physical);
690        property!(border_bottom, style, &val.bottom, Physical);
691        property!(border_left, style, &val.left, Physical);
692        self.border_block_start.style = None;
693        self.border_block_end.style = None;
694        self.border_inline_start.style = None;
695        self.border_inline_end.style = None;
696        self.has_any = true;
697      }
698      BorderColor(val) => {
699        property!(border_top, color, &val.top, Physical);
700        property!(border_right, color, &val.right, Physical);
701        property!(border_bottom, color, &val.bottom, Physical);
702        property!(border_left, color, &val.left, Physical);
703        self.border_block_start.color = None;
704        self.border_block_end.color = None;
705        self.border_inline_start.color = None;
706        self.border_inline_end.color = None;
707        self.has_any = true;
708      }
709      Border(val) => {
710        // dest.clear();
711        self.border_top.set_border(val);
712        self.border_bottom.set_border(val);
713        self.border_left.set_border(val);
714        self.border_right.set_border(val);
715        self.border_block_start.reset();
716        self.border_block_end.reset();
717        self.border_inline_start.reset();
718        self.border_inline_end.reset();
719
720        // Setting the `border` property resets `border-image`.
721        self.border_image_handler.reset();
722        self.has_any = true;
723      }
724      Unparsed(val) if is_border_property(&val.property_id) => {
725        self.flush(dest, context);
726        self.flush_unparsed(&val, dest, context);
727      }
728      _ => {
729        if self.border_image_handler.will_flush(property) {
730          self.flush(dest, context);
731        }
732
733        return self.border_image_handler.handle_property(property, dest, context)
734          || self.border_radius_handler.handle_property(property, dest, context);
735      }
736    }
737
738    true
739  }
740
741  fn finalize(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
742    self.flush(dest, context);
743    self.flushed_properties = BorderProperty::empty();
744    self.border_image_handler.finalize(dest, context);
745    self.border_radius_handler.finalize(dest, context);
746  }
747}
748
749impl<'i> BorderHandler<'i> {
750  fn flush(&mut self, dest: &mut DeclarationList, context: &mut PropertyHandlerContext<'i, '_>) {
751    if !self.has_any {
752      return;
753    }
754
755    self.has_any = false;
756
757    let logical_supported = !context.should_compile_logical(Feature::LogicalBorders);
758    let logical_shorthand_supported = !context.should_compile_logical(Feature::LogicalBorderShorthand);
759    macro_rules! logical_prop {
760      ($ltr: ident, $ltr_key: ident, $rtl: ident, $rtl_key: ident, $val: expr) => {{
761        context.add_logical_rule(Property::$ltr($val.clone()), Property::$rtl($val.clone()));
762      }};
763    }
764
765    macro_rules! push {
766      ($prop: ident, $val: expr) => {{
767        self.flushed_properties.insert(BorderProperty::$prop);
768        dest.push(Property::$prop($val));
769      }};
770    }
771
772    macro_rules! fallbacks {
773      ($prop: ident => $val: expr) => {{
774        let mut val = $val;
775        if !self.flushed_properties.contains(BorderProperty::$prop) {
776          let fallbacks = val.get_fallbacks(context.targets);
777          for fallback in fallbacks {
778            dest.push(Property::$prop(fallback))
779          }
780        }
781        push!($prop, val);
782      }};
783    }
784
785    macro_rules! prop {
786      (BorderInlineStart => $val: expr) => {
787        if logical_supported {
788          fallbacks!(BorderInlineStart => $val);
789        } else {
790          logical_prop!(BorderLeft, border_left, BorderRight, border_right, $val);
791        }
792      };
793      (BorderInlineStartWidth => $val: expr) => {
794        if logical_supported {
795          push!(BorderInlineStartWidth, $val);
796        } else {
797          logical_prop!(BorderLeftWidth, border_left_width, BorderRightWidth, border_right_width, $val);
798        }
799      };
800      (BorderInlineStartColor => $val: expr) => {
801        if logical_supported {
802          fallbacks!(BorderInlineStartColor => $val);
803        } else {
804          logical_prop!(BorderLeftColor, border_left_color, BorderRightColor, border_right_color, $val);
805        }
806      };
807      (BorderInlineStartStyle => $val: expr) => {
808        if logical_supported {
809          push!(BorderInlineStartStyle, $val);
810        } else {
811          logical_prop!(BorderLeftStyle, border_left_style, BorderRightStyle, border_right_style, $val);
812        }
813      };
814      (BorderInlineEnd => $val: expr) => {
815        if logical_supported {
816          fallbacks!(BorderInlineEnd => $val);
817        } else {
818          logical_prop!(BorderRight, border_right, BorderLeft, border_left, $val);
819        }
820      };
821      (BorderInlineEndWidth => $val: expr) => {
822        if logical_supported {
823          push!(BorderInlineEndWidth, $val);
824        } else {
825          logical_prop!(BorderRightWidth, border_right_width, BorderLeftWidth, border_left_width, $val);
826        }
827      };
828      (BorderInlineEndColor => $val: expr) => {
829        if logical_supported {
830          fallbacks!(BorderInlineEndColor => $val);
831        } else {
832          logical_prop!(BorderRightColor, border_right_color, BorderLeftColor, border_left_color, $val);
833        }
834      };
835      (BorderInlineEndStyle => $val: expr) => {
836        if logical_supported {
837          push!(BorderInlineEndStyle, $val);
838        } else {
839          logical_prop!(BorderRightStyle, border_right_style, BorderLeftStyle, border_left_style, $val);
840        }
841      };
842      (BorderBlockStart => $val: expr) => {
843        if logical_supported {
844          fallbacks!(BorderBlockStart => $val);
845        } else {
846          fallbacks!(BorderTop => $val);
847        }
848      };
849      (BorderBlockStartWidth => $val: expr) => {
850        if logical_supported {
851          push!(BorderBlockStartWidth, $val);
852        } else {
853          push!(BorderTopWidth, $val);
854        }
855      };
856      (BorderBlockStartColor => $val: expr) => {
857        if logical_supported {
858          fallbacks!(BorderBlockStartColor => $val);
859        } else {
860          fallbacks!(BorderTopColor => $val);
861        }
862      };
863      (BorderBlockStartStyle => $val: expr) => {
864        if logical_supported {
865          push!(BorderBlockStartStyle, $val);
866        } else {
867          push!(BorderTopStyle, $val);
868        }
869      };
870      (BorderBlockEnd => $val: expr) => {
871        if logical_supported {
872          fallbacks!(BorderBlockEnd => $val);
873        } else {
874          fallbacks!(BorderBottom => $val);
875        }
876      };
877      (BorderBlockEndWidth => $val: expr) => {
878        if logical_supported {
879          push!(BorderBlockEndWidth, $val);
880        } else {
881          push!(BorderBottomWidth, $val);
882        }
883      };
884      (BorderBlockEndColor => $val: expr) => {
885        if logical_supported {
886          fallbacks!(BorderBlockEndColor => $val);
887        } else {
888          fallbacks!(BorderBottomColor => $val);
889        }
890      };
891      (BorderBlockEndStyle => $val: expr) => {
892        if logical_supported {
893          push!(BorderBlockEndStyle, $val);
894        } else {
895          push!(BorderBottomStyle, $val);
896        }
897      };
898      (BorderLeftColor => $val: expr) => {
899        fallbacks!(BorderLeftColor => $val);
900      };
901      (BorderRightColor => $val: expr) => {
902        fallbacks!(BorderRightColor => $val);
903      };
904      (BorderTopColor => $val: expr) => {
905        fallbacks!(BorderTopColor => $val);
906      };
907      (BorderBottomColor => $val: expr) => {
908        fallbacks!(BorderBottomColor => $val);
909      };
910      (BorderColor => $val: expr) => {
911        fallbacks!(BorderColor => $val);
912      };
913      (BorderBlockColor => $val: expr) => {
914        fallbacks!(BorderBlockColor => $val);
915      };
916      (BorderInlineColor => $val: expr) => {
917        fallbacks!(BorderInlineColor => $val);
918      };
919      (BorderLeft => $val: expr) => {
920        fallbacks!(BorderLeft => $val);
921      };
922      (BorderRight => $val: expr) => {
923        fallbacks!(BorderRight => $val);
924      };
925      (BorderTop => $val: expr) => {
926        fallbacks!(BorderTop => $val);
927      };
928      (BorderBottom => $val: expr) => {
929        fallbacks!(BorderBottom => $val);
930      };
931      (BorderBlockStart => $val: expr) => {
932        fallbacks!(BorderBlockStart => $val);
933      };
934      (BorderBlockEnd => $val: expr) => {
935        fallbacks!(BorderBlockEnd => $val);
936      };
937      (BorderInlineStart => $val: expr) => {
938        fallbacks!(BorderInlineStart => $val);
939      };
940      (BorderInlineEnd => $val: expr) => {
941        fallbacks!(BorderInlineEnd => $val);
942      };
943      (BorderInline => $val: expr) => {
944        fallbacks!(BorderInline => $val);
945      };
946      (BorderBlock => $val: expr) => {
947        fallbacks!(BorderBlock => $val);
948      };
949      (Border => $val: expr) => {
950        fallbacks!(Border => $val);
951      };
952      ($prop: ident => $val: expr) => {
953        push!($prop, $val);
954      };
955    }
956
957    macro_rules! flush_category {
958      (
959        $block_start_prop: ident,
960        $block_start_width: ident,
961        $block_start_style: ident,
962        $block_start_color: ident,
963        $block_start: expr,
964        $block_end_prop: ident,
965        $block_end_width: ident,
966        $block_end_style: ident,
967        $block_end_color: ident,
968        $block_end: expr,
969        $inline_start_prop: ident,
970        $inline_start_width: ident,
971        $inline_start_style: ident,
972        $inline_start_color: ident,
973        $inline_start: expr,
974        $inline_end_prop: ident,
975        $inline_end_width: ident,
976        $inline_end_style: ident,
977        $inline_end_color: ident,
978        $inline_end: expr,
979        $is_logical: expr
980      ) => {
981        macro_rules! shorthand {
982          ($prop: ident, $key: ident) => {{
983            let has_prop = $block_start.$key.is_some() && $block_end.$key.is_some() && $inline_start.$key.is_some() && $inline_end.$key.is_some();
984            if has_prop {
985              if !$is_logical || ($block_start.$key == $block_end.$key && $block_end.$key == $inline_start.$key && $inline_start.$key == $inline_end.$key) {
986                let rect = $prop {
987                  top: std::mem::take(&mut $block_start.$key).unwrap(),
988                  right: std::mem::take(&mut $inline_end.$key).unwrap(),
989                  bottom: std::mem::take(&mut $block_end.$key).unwrap(),
990                  left: std::mem::take(&mut $inline_start.$key).unwrap()
991                };
992                prop!($prop => rect);
993              }
994            }
995          }};
996        }
997
998        macro_rules! logical_shorthand {
999          ($prop: ident, $key: ident, $start: expr, $end: expr) => {{
1000            let has_prop = $start.$key.is_some() && $end.$key.is_some();
1001            if has_prop {
1002              prop!($prop => $prop {
1003                start: std::mem::take(&mut $start.$key).unwrap(),
1004                end: std::mem::take(&mut $end.$key).unwrap(),
1005              });
1006              $end.$key = None;
1007            }
1008            has_prop
1009          }};
1010        }
1011
1012        if $block_start.is_valid() && $block_end.is_valid() && $inline_start.is_valid() && $inline_end.is_valid() {
1013          let top_eq_bottom = $block_start == $block_end;
1014          let left_eq_right = $inline_start == $inline_end;
1015          let top_eq_left = $block_start == $inline_start;
1016          let top_eq_right = $block_start == $inline_end;
1017          let bottom_eq_left = $block_end == $inline_start;
1018          let bottom_eq_right = $block_end == $inline_end;
1019
1020          macro_rules! is_eq {
1021            ($key: ident) => {
1022              $block_start.$key == $block_end.$key &&
1023              $inline_start.$key == $inline_end.$key &&
1024              $inline_start.$key == $block_start.$key
1025            };
1026          }
1027
1028          macro_rules! prop_diff {
1029            ($border: expr, $fallback: expr, $border_fallback: literal) => {
1030              if !$is_logical && is_eq!(color) && is_eq!(style) {
1031                prop!(Border => $border.to_border());
1032                shorthand!(BorderWidth, width);
1033              } else if !$is_logical && is_eq!(width) && is_eq!(style) {
1034                prop!(Border => $border.to_border());
1035                shorthand!(BorderColor, color);
1036              } else if !$is_logical && is_eq!(width) && is_eq!(color) {
1037                prop!(Border => $border.to_border());
1038                shorthand!(BorderStyle, style);
1039              } else {
1040                if $border_fallback {
1041                  prop!(Border => $border.to_border());
1042                }
1043                $fallback
1044              }
1045            };
1046          }
1047
1048          macro_rules! side_diff {
1049            ($border: expr, $other: expr, $prop: ident, $width: ident, $style: ident, $color: ident) => {
1050              let eq_width = $border.width == $other.width;
1051              let eq_style = $border.style == $other.style;
1052              let eq_color = $border.color == $other.color;
1053
1054              // If only one of the sub-properties is different, only emit that.
1055              // Otherwise, emit the full border value.
1056              if eq_width && eq_style {
1057                prop!($color => $other.color.clone().unwrap());
1058              } else if eq_width && eq_color {
1059                prop!($style => $other.style.clone().unwrap());
1060              } else if eq_style && eq_color {
1061                prop!($width => $other.width.clone().unwrap());
1062              } else {
1063                prop!($prop => $other.to_border());
1064              }
1065            };
1066          }
1067
1068          if top_eq_bottom && top_eq_left && top_eq_right {
1069            prop!(Border => $block_start.to_border());
1070          } else if top_eq_bottom && top_eq_left {
1071            prop!(Border => $block_start.to_border());
1072            side_diff!($block_start, $inline_end, $inline_end_prop, $inline_end_width, $inline_end_style, $inline_end_color);
1073          } else if top_eq_bottom && top_eq_right {
1074            prop!(Border => $block_start.to_border());
1075            side_diff!($block_start, $inline_start, $inline_start_prop, $inline_start_width, $inline_start_style, $inline_start_color);
1076          } else if left_eq_right && bottom_eq_left {
1077            prop!(Border => $inline_start.to_border());
1078            side_diff!($inline_start, $block_start, $block_start_prop, $block_start_width, $block_start_style, $block_start_color);
1079          } else if left_eq_right && top_eq_left {
1080            prop!(Border => $inline_start.to_border());
1081            side_diff!($inline_start, $block_end, $block_end_prop, $block_end_width, $block_end_style, $block_end_color);
1082          } else if top_eq_bottom {
1083            prop_diff!($block_start, {
1084              // Try to use border-inline shorthands for the opposide direction if possible.
1085              let mut handled = false;
1086              if $is_logical {
1087                let mut diff = 0;
1088                if $inline_start.width != $block_start.width || $inline_end.width != $block_start.width {
1089                  diff += 1;
1090                }
1091                if $inline_start.style != $block_start.style || $inline_end.style != $block_start.style {
1092                  diff += 1;
1093                }
1094                if $inline_start.color != $block_start.color || $inline_end.color != $block_start.color {
1095                  diff += 1;
1096                }
1097
1098                if diff == 1 {
1099                  if $inline_start.width != $block_start.width {
1100                    prop!(BorderInlineWidth => BorderInlineWidth {
1101                      start: $inline_start.width.clone().unwrap(),
1102                      end: $inline_end.width.clone().unwrap(),
1103                    });
1104                    handled = true;
1105                  } else if $inline_start.style != $block_start.style {
1106                    prop!(BorderInlineStyle => BorderInlineStyle {
1107                      start: $inline_start.style.clone().unwrap(),
1108                      end: $inline_end.style.clone().unwrap()
1109                    });
1110                    handled = true;
1111                  } else if $inline_start.color != $block_start.color {
1112                    prop!(BorderInlineColor => BorderInlineColor {
1113                      start: $inline_start.color.clone().unwrap(),
1114                      end: $inline_end.color.clone().unwrap()
1115                    });
1116                    handled = true;
1117                  }
1118                } else if diff > 1 && $inline_start.width == $inline_end.width && $inline_start.style == $inline_end.style && $inline_start.color == $inline_end.color {
1119                  prop!(BorderInline => $inline_start.to_border());
1120                  handled = true;
1121                }
1122              }
1123
1124              if !handled {
1125                side_diff!($block_start, $inline_start, $inline_start_prop, $inline_start_width, $inline_start_style, $inline_start_color);
1126                side_diff!($block_start, $inline_end, $inline_end_prop, $inline_end_width, $inline_end_style, $inline_end_color);
1127              }
1128            }, true);
1129          } else if left_eq_right {
1130            prop_diff!($inline_start, {
1131              // We know already that top != bottom, so no need to try to use border-block.
1132              side_diff!($inline_start, $block_start, $block_start_prop, $block_start_width, $block_start_style, $block_start_color);
1133              side_diff!($inline_start, $block_end, $block_end_prop, $block_end_width, $block_end_style, $block_end_color);
1134            }, true);
1135          } else if bottom_eq_right {
1136            prop_diff!($block_end, {
1137              side_diff!($block_end, $block_start, $block_start_prop, $block_start_width, $block_start_style, $block_start_color);
1138              side_diff!($block_end, $inline_start, $inline_start_prop, $inline_start_width, $inline_start_style, $inline_start_color);
1139            }, true);
1140          } else {
1141            prop_diff!($block_start, {
1142              prop!($block_start_prop => $block_start.to_border());
1143              prop!($block_end_prop => $block_end.to_border());
1144              prop!($inline_start_prop => $inline_start.to_border());
1145              prop!($inline_end_prop => $inline_end.to_border());
1146            }, false);
1147          }
1148        } else {
1149          shorthand!(BorderStyle, style);
1150          shorthand!(BorderWidth, width);
1151          shorthand!(BorderColor, color);
1152
1153          macro_rules! side {
1154            ($val: expr, $shorthand: ident, $width: ident, $style: ident, $color: ident) => {
1155              if $val.is_valid() {
1156                prop!($shorthand => $val.to_border());
1157              } else {
1158                if let Some(style) = &$val.style {
1159                  prop!($style => style.clone());
1160                }
1161
1162                if let Some(width) = &$val.width {
1163                  prop!($width => width.clone());
1164                }
1165
1166                if let Some(color) = &$val.color {
1167                  prop!($color => color.clone());
1168                }
1169              }
1170            };
1171          }
1172
1173          if $is_logical && $block_start == $block_end && $block_start.is_valid() {
1174            if logical_supported {
1175              if logical_shorthand_supported {
1176                prop!(BorderBlock => $block_start.to_border());
1177              } else {
1178                prop!(BorderBlockStart => $block_start.to_border());
1179                prop!(BorderBlockEnd => $block_start.to_border());
1180              }
1181            } else {
1182              prop!(BorderTop => $block_start.to_border());
1183              prop!(BorderBottom => $block_start.to_border());
1184            }
1185          } else {
1186            if $is_logical && logical_shorthand_supported && !$block_start.is_valid() && !$block_end.is_valid() {
1187              logical_shorthand!(BorderBlockStyle, style, $block_start, $block_end);
1188              logical_shorthand!(BorderBlockWidth, width, $block_start, $block_end);
1189              logical_shorthand!(BorderBlockColor, color, $block_start, $block_end);
1190            }
1191
1192            side!($block_start, $block_start_prop, $block_start_width, $block_start_style, $block_start_color);
1193            side!($block_end, $block_end_prop, $block_end_width, $block_end_style, $block_end_color);
1194          }
1195
1196          if $is_logical && $inline_start == $inline_end && $inline_start.is_valid() {
1197            if logical_supported {
1198              if logical_shorthand_supported {
1199                prop!(BorderInline => $inline_start.to_border());
1200              } else {
1201                prop!(BorderInlineStart => $inline_start.to_border());
1202                prop!(BorderInlineEnd => $inline_start.to_border());
1203              }
1204            } else {
1205              prop!(BorderLeft => $inline_start.to_border());
1206              prop!(BorderRight => $inline_start.to_border());
1207            }
1208          } else {
1209            if $is_logical && !$inline_start.is_valid() && !$inline_end.is_valid() {
1210              if logical_shorthand_supported {
1211                logical_shorthand!(BorderInlineStyle, style, $inline_start, $inline_end);
1212                logical_shorthand!(BorderInlineWidth, width, $inline_start, $inline_end);
1213                logical_shorthand!(BorderInlineColor, color, $inline_start, $inline_end);
1214              } else {
1215                // If both values of an inline logical property are equal, then we can just convert them to physical properties.
1216                macro_rules! inline_prop {
1217                  ($key: ident, $left: ident, $right: ident) => {
1218                    if $inline_start.$key.is_some() && $inline_start.$key == $inline_end.$key {
1219                      prop!($left => std::mem::take(&mut $inline_start.$key).unwrap());
1220                      prop!($right => std::mem::take(&mut $inline_end.$key).unwrap());
1221                    }
1222                  }
1223                }
1224
1225                inline_prop!(style, BorderLeftStyle, BorderRightStyle);
1226                inline_prop!(width, BorderLeftWidth, BorderRightWidth);
1227                inline_prop!(color, BorderLeftColor, BorderRightColor);
1228              }
1229            }
1230
1231            side!($inline_start, $inline_start_prop, $inline_start_width, $inline_start_style, $inline_start_color);
1232            side!($inline_end, $inline_end_prop, $inline_end_width, $inline_end_style, $inline_end_color);
1233          }
1234        }
1235      };
1236    }
1237
1238    flush_category!(
1239      BorderTop,
1240      BorderTopWidth,
1241      BorderTopStyle,
1242      BorderTopColor,
1243      self.border_top,
1244      BorderBottom,
1245      BorderBottomWidth,
1246      BorderBottomStyle,
1247      BorderBottomColor,
1248      self.border_bottom,
1249      BorderLeft,
1250      BorderLeftWidth,
1251      BorderLeftStyle,
1252      BorderLeftColor,
1253      self.border_left,
1254      BorderRight,
1255      BorderRightWidth,
1256      BorderRightStyle,
1257      BorderRightColor,
1258      self.border_right,
1259      false
1260    );
1261
1262    flush_category!(
1263      BorderBlockStart,
1264      BorderBlockStartWidth,
1265      BorderBlockStartStyle,
1266      BorderBlockStartColor,
1267      self.border_block_start,
1268      BorderBlockEnd,
1269      BorderBlockEndWidth,
1270      BorderBlockEndStyle,
1271      BorderBlockEndColor,
1272      self.border_block_end,
1273      BorderInlineStart,
1274      BorderInlineStartWidth,
1275      BorderInlineStartStyle,
1276      BorderInlineStartColor,
1277      self.border_inline_start,
1278      BorderInlineEnd,
1279      BorderInlineEndWidth,
1280      BorderInlineEndStyle,
1281      BorderInlineEndColor,
1282      self.border_inline_end,
1283      true
1284    );
1285
1286    self.border_top.reset();
1287    self.border_bottom.reset();
1288    self.border_left.reset();
1289    self.border_right.reset();
1290    self.border_block_start.reset();
1291    self.border_block_end.reset();
1292    self.border_inline_start.reset();
1293    self.border_inline_end.reset();
1294  }
1295
1296  fn flush_unparsed(
1297    &mut self,
1298    unparsed: &UnparsedProperty<'i>,
1299    dest: &mut DeclarationList<'i>,
1300    context: &mut PropertyHandlerContext<'i, '_>,
1301  ) {
1302    let logical_supported = !context.should_compile_logical(Feature::LogicalBorders);
1303    if logical_supported {
1304      let mut unparsed = unparsed.clone();
1305      context.add_unparsed_fallbacks(&mut unparsed);
1306      self
1307        .flushed_properties
1308        .insert(BorderProperty::try_from(&unparsed.property_id).unwrap());
1309      dest.push(Property::Unparsed(unparsed));
1310      return;
1311    }
1312
1313    macro_rules! prop {
1314      ($id: ident) => {{
1315        let mut unparsed = unparsed.with_property_id(PropertyId::$id);
1316        context.add_unparsed_fallbacks(&mut unparsed);
1317        dest.push(Property::Unparsed(unparsed));
1318        self.flushed_properties.insert(BorderProperty::$id);
1319      }};
1320    }
1321
1322    macro_rules! logical_prop {
1323      ($ltr: ident, $ltr_key: ident, $rtl: ident, $rtl_key: ident) => {{
1324        context.add_logical_rule(
1325          Property::Unparsed(unparsed.with_property_id(PropertyId::$ltr)),
1326          Property::Unparsed(unparsed.with_property_id(PropertyId::$rtl)),
1327        );
1328      }};
1329    }
1330
1331    use PropertyId::*;
1332    match &unparsed.property_id {
1333      BorderInlineStart => logical_prop!(BorderLeft, border_left, BorderRight, border_right),
1334      BorderInlineStartWidth => {
1335        logical_prop!(BorderLeftWidth, border_left_width, BorderRightWidth, border_right_width)
1336      }
1337      BorderInlineStartColor => {
1338        logical_prop!(BorderLeftColor, border_left_color, BorderRightColor, border_right_color)
1339      }
1340      BorderInlineStartStyle => {
1341        logical_prop!(BorderLeftStyle, border_left_style, BorderRightStyle, border_right_style)
1342      }
1343      BorderInlineEnd => logical_prop!(BorderRight, border_right, BorderLeft, border_left),
1344      BorderInlineEndWidth => {
1345        logical_prop!(BorderRightWidth, border_right_width, BorderLeftWidth, border_left_width)
1346      }
1347      BorderInlineEndColor => {
1348        logical_prop!(BorderRightColor, border_right_color, BorderLeftColor, border_left_color)
1349      }
1350      BorderInlineEndStyle => {
1351        logical_prop!(BorderRightStyle, border_right_style, BorderLeftStyle, border_left_style)
1352      }
1353      BorderBlockStart => prop!(BorderTop),
1354      BorderBlockStartWidth => prop!(BorderTopWidth),
1355      BorderBlockStartColor => prop!(BorderTopColor),
1356      BorderBlockStartStyle => prop!(BorderTopStyle),
1357      BorderBlockEnd => prop!(BorderBottom),
1358      BorderBlockEndWidth => prop!(BorderBottomWidth),
1359      BorderBlockEndColor => prop!(BorderBottomColor),
1360      BorderBlockEndStyle => prop!(BorderBottomStyle),
1361      property_id => {
1362        let mut unparsed = unparsed.clone();
1363        context.add_unparsed_fallbacks(&mut unparsed);
1364        dest.push(Property::Unparsed(unparsed));
1365        self.flushed_properties.insert(BorderProperty::try_from(property_id).unwrap());
1366      }
1367    }
1368  }
1369}
1370
1371fn is_border_property(property_id: &PropertyId) -> bool {
1372  match property_id {
1373    PropertyId::BorderTopColor
1374    | PropertyId::BorderBottomColor
1375    | PropertyId::BorderLeftColor
1376    | PropertyId::BorderRightColor
1377    | PropertyId::BorderBlockStartColor
1378    | PropertyId::BorderBlockEndColor
1379    | PropertyId::BorderBlockColor
1380    | PropertyId::BorderInlineStartColor
1381    | PropertyId::BorderInlineEndColor
1382    | PropertyId::BorderInlineColor
1383    | PropertyId::BorderTopWidth
1384    | PropertyId::BorderBottomWidth
1385    | PropertyId::BorderLeftWidth
1386    | PropertyId::BorderRightWidth
1387    | PropertyId::BorderBlockStartWidth
1388    | PropertyId::BorderBlockEndWidth
1389    | PropertyId::BorderBlockWidth
1390    | PropertyId::BorderInlineStartWidth
1391    | PropertyId::BorderInlineEndWidth
1392    | PropertyId::BorderInlineWidth
1393    | PropertyId::BorderTopStyle
1394    | PropertyId::BorderBottomStyle
1395    | PropertyId::BorderLeftStyle
1396    | PropertyId::BorderRightStyle
1397    | PropertyId::BorderBlockStartStyle
1398    | PropertyId::BorderBlockEndStyle
1399    | PropertyId::BorderBlockStyle
1400    | PropertyId::BorderInlineStartStyle
1401    | PropertyId::BorderInlineEndStyle
1402    | PropertyId::BorderInlineStyle
1403    | PropertyId::BorderTop
1404    | PropertyId::BorderBottom
1405    | PropertyId::BorderLeft
1406    | PropertyId::BorderRight
1407    | PropertyId::BorderBlockStart
1408    | PropertyId::BorderBlockEnd
1409    | PropertyId::BorderInlineStart
1410    | PropertyId::BorderInlineEnd
1411    | PropertyId::BorderBlock
1412    | PropertyId::BorderInline
1413    | PropertyId::BorderWidth
1414    | PropertyId::BorderStyle
1415    | PropertyId::BorderColor
1416    | PropertyId::Border => true,
1417    _ => false,
1418  }
1419}