1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! CSS properties related to overflow.

use super::{Property, PropertyId};
use crate::compat::Feature;
use crate::context::PropertyHandlerContext;
use crate::declaration::{DeclarationBlock, DeclarationList};
use crate::error::{ParserError, PrinterError};
use crate::macros::{define_shorthand, enum_property};
use crate::printer::Printer;
use crate::targets::Browsers;
use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss};
use cssparser::*;

enum_property! {
  /// An [overflow](https://www.w3.org/TR/css-overflow-3/#overflow-properties) keyword
  /// as used in the `overflow-x`, `overflow-y`, and `overflow` properties.
  pub enum OverflowKeyword {
    /// Overflowing content is visible.
    Visible,
    /// Overflowing content is hidden. Programmatic scrolling is allowed.
    Hidden,
    /// Overflowing content is clipped. Programmatic scrolling is not allowed.
    Clip,
    /// The element is scrollable.
    Scroll,
    /// Overflowing content scrolls if needed.
    Auto,
  }
}

define_shorthand! {
  /// A value for the [overflow](https://www.w3.org/TR/css-overflow-3/#overflow-properties) shorthand property.
  pub struct Overflow {
    /// The overflow mode for the x direction.
    x: OverflowX(OverflowKeyword),
    /// The overflow mode for the y direction.
    y: OverflowY(OverflowKeyword),
  }
}

impl<'i> Parse<'i> for Overflow {
  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
    let x = OverflowKeyword::parse(input)?;
    let y = input.try_parse(OverflowKeyword::parse).unwrap_or_else(|_| x.clone());
    Ok(Overflow { x, y })
  }
}

impl ToCss for Overflow {
  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
  where
    W: std::fmt::Write,
  {
    self.x.to_css(dest)?;
    if self.y != self.x {
      dest.write_char(' ')?;
      self.y.to_css(dest)?;
    }
    Ok(())
  }
}

enum_property! {
  /// A value for the [text-overflow](https://www.w3.org/TR/css-overflow-3/#text-overflow) property.
  pub enum TextOverflow {
    /// Overflowing text is clipped.
    Clip,
    /// Overflowing text is truncated with an ellipsis.
    Ellipsis,
  }
}

#[derive(Default)]
pub(crate) struct OverflowHandler {
  targets: Option<Browsers>,
  x: Option<OverflowKeyword>,
  y: Option<OverflowKeyword>,
}

impl OverflowHandler {
  pub fn new(targets: Option<Browsers>) -> OverflowHandler {
    OverflowHandler {
      targets,
      ..OverflowHandler::default()
    }
  }
}

impl<'i> PropertyHandler<'i> for OverflowHandler {
  fn handle_property(
    &mut self,
    property: &Property<'i>,
    dest: &mut DeclarationList<'i>,
    context: &mut PropertyHandlerContext<'i, '_>,
  ) -> bool {
    use Property::*;

    match property {
      OverflowX(val) => self.x = Some(*val),
      OverflowY(val) => self.y = Some(*val),
      Overflow(val) => {
        self.x = Some(val.x);
        self.y = Some(val.y);
      }
      Unparsed(val)
        if matches!(
          val.property_id,
          PropertyId::OverflowX | PropertyId::OverflowY | PropertyId::Overflow
        ) =>
      {
        self.finalize(dest, context);
        dest.push(property.clone());
      }
      _ => return false,
    }

    true
  }

  fn finalize(&mut self, dest: &mut DeclarationList, _: &mut PropertyHandlerContext<'i, '_>) {
    if self.x.is_none() && self.y.is_none() {
      return;
    }

    let x = std::mem::take(&mut self.x);
    let y = std::mem::take(&mut self.y);

    match (x, y) {
      // Only use shorthand syntax if the x and y values are the
      // same or the two-value syntax is supported by all targets.
      (Some(x), Some(y))
        if x == y || self.targets.is_none() || Feature::OverflowShorthand.is_compatible(self.targets.unwrap()) =>
      {
        dest.push(Property::Overflow(Overflow { x, y }))
      }
      _ => {
        if let Some(x) = x {
          dest.push(Property::OverflowX(x))
        }

        if let Some(y) = y {
          dest.push(Property::OverflowY(y))
        }
      }
    }
  }
}