style/values/specified/
list.rs

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//! `list` specified values.
6
7#[cfg(feature = "gecko")]
8use crate::counter_style::{CounterStyle, CounterStyleParsingFlags};
9use crate::parser::{Parse, ParserContext};
10#[cfg(feature = "servo")]
11use crate::properties::longhands::list_style_type::SpecifiedValue as ListStyleType;
12use cssparser::{Parser, Token};
13use style_traits::{ParseError, StyleParseErrorKind};
14
15/// Specified and computed `list-style-type` property.
16#[cfg(feature = "gecko")]
17#[derive(
18    Clone,
19    Debug,
20    Eq,
21    MallocSizeOf,
22    PartialEq,
23    SpecifiedValueInfo,
24    ToComputedValue,
25    ToCss,
26    ToResolvedValue,
27    ToShmem,
28)]
29#[repr(transparent)]
30pub struct ListStyleType(pub CounterStyle);
31
32#[cfg(feature = "gecko")]
33impl ListStyleType {
34    /// Initial specified value for `list-style-type`.
35    #[inline]
36    pub fn disc() -> Self {
37        Self(CounterStyle::disc())
38    }
39
40    /// none value.
41    #[inline]
42    pub fn none() -> Self {
43        Self(CounterStyle::None)
44    }
45
46    /// Convert from gecko keyword to list-style-type.
47    ///
48    /// This should only be used for mapping type attribute to list-style-type, and thus only
49    /// values possible in that attribute is considered here.
50    pub fn from_gecko_keyword(value: u32) -> Self {
51        use crate::gecko_bindings::structs;
52        use crate::values::CustomIdent;
53        let v8 = value as u8;
54        if v8 == structs::ListStyle_None {
55            return Self::none();
56        }
57
58        Self(CounterStyle::Name(CustomIdent(match v8 {
59            structs::ListStyle_Disc => atom!("disc"),
60            structs::ListStyle_Circle => atom!("circle"),
61            structs::ListStyle_Square => atom!("square"),
62            structs::ListStyle_Decimal => atom!("decimal"),
63            structs::ListStyle_LowerRoman => atom!("lower-roman"),
64            structs::ListStyle_UpperRoman => atom!("upper-roman"),
65            structs::ListStyle_LowerAlpha => atom!("lower-alpha"),
66            structs::ListStyle_UpperAlpha => atom!("upper-alpha"),
67            _ => unreachable!("Unknown counter style keyword value"),
68        })))
69    }
70
71    /// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)
72    #[inline]
73    pub fn is_bullet(&self) -> bool {
74        self.0.is_bullet()
75    }
76}
77
78#[cfg(feature = "gecko")]
79impl Parse for ListStyleType {
80    fn parse<'i, 't>(
81        context: &ParserContext,
82        input: &mut Parser<'i, 't>,
83    ) -> Result<Self, ParseError<'i>> {
84        let flags = CounterStyleParsingFlags::ALLOW_NONE | CounterStyleParsingFlags::ALLOW_STRING;
85        Ok(Self(CounterStyle::parse(context, input, flags)?))
86    }
87}
88
89#[cfg(feature = "servo")]
90impl ListStyleType {
91    /// Initial specified value for `list-style-type`.
92    #[inline]
93    pub fn disc() -> Self {
94        Self::Disc
95    }
96
97    /// none value.
98    #[inline]
99    pub fn none() -> Self {
100        Self::None
101    }
102}
103
104/// A quote pair.
105#[derive(
106    Clone,
107    Debug,
108    MallocSizeOf,
109    PartialEq,
110    SpecifiedValueInfo,
111    ToComputedValue,
112    ToCss,
113    ToResolvedValue,
114    ToShmem,
115)]
116#[repr(C)]
117pub struct QuotePair {
118    /// The opening quote.
119    pub opening: crate::OwnedStr,
120
121    /// The closing quote.
122    pub closing: crate::OwnedStr,
123}
124
125/// List of quote pairs for the specified/computed value of `quotes` property.
126#[derive(
127    Clone,
128    Debug,
129    Default,
130    MallocSizeOf,
131    PartialEq,
132    SpecifiedValueInfo,
133    ToComputedValue,
134    ToCss,
135    ToResolvedValue,
136    ToShmem,
137)]
138#[repr(transparent)]
139pub struct QuoteList(
140    #[css(iterable, if_empty = "none")]
141    #[ignore_malloc_size_of = "Arc"]
142    pub crate::ArcSlice<QuotePair>,
143);
144
145/// Specified and computed `quotes` property: `auto`, `none`, or a list
146/// of characters.
147#[derive(
148    Clone,
149    Debug,
150    MallocSizeOf,
151    PartialEq,
152    SpecifiedValueInfo,
153    ToComputedValue,
154    ToCss,
155    ToResolvedValue,
156    ToShmem,
157)]
158#[repr(C)]
159pub enum Quotes {
160    /// list of quote pairs
161    QuoteList(QuoteList),
162    /// auto (use lang-dependent quote marks)
163    Auto,
164}
165
166impl Parse for Quotes {
167    fn parse<'i, 't>(
168        _: &ParserContext,
169        input: &mut Parser<'i, 't>,
170    ) -> Result<Quotes, ParseError<'i>> {
171        if input
172            .try_parse(|input| input.expect_ident_matching("auto"))
173            .is_ok()
174        {
175            return Ok(Quotes::Auto);
176        }
177
178        if input
179            .try_parse(|input| input.expect_ident_matching("none"))
180            .is_ok()
181        {
182            return Ok(Quotes::QuoteList(QuoteList::default()));
183        }
184
185        let mut quotes = Vec::new();
186        loop {
187            let location = input.current_source_location();
188            let opening = match input.next() {
189                Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned().into(),
190                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
191                Err(_) => break,
192            };
193
194            let closing = input.expect_string()?.as_ref().to_owned().into();
195            quotes.push(QuotePair { opening, closing });
196        }
197
198        if !quotes.is_empty() {
199            Ok(Quotes::QuoteList(QuoteList(crate::ArcSlice::from_iter(
200                quotes.into_iter(),
201            ))))
202        } else {
203            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
204        }
205    }
206}