style/stylesheets/
layer_rule.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//! A [`@layer`][layer] rule.
6//!
7//! [layer]: https://drafts.csswg.org/css-cascade-5/#layering
8
9use crate::derives::*;
10use crate::parser::{Parse, ParserContext};
11use crate::shared_lock::{DeepCloneWithLock, Locked};
12use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
13use crate::values::AtomIdent;
14
15use super::CssRules;
16
17use cssparser::{Parser, SourceLocation, Token};
18use servo_arc::Arc;
19use smallvec::SmallVec;
20use std::fmt::{self, Write};
21use style_traits::{CssWriter, ParseError, ToCss};
22
23/// The order of a given layer. We use 16 bits so that we can pack LayerOrder
24/// and CascadeLevel in a single 32-bit struct. If we need more bits we can go
25/// back to packing CascadeLevel in a single byte as we did before.
26#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord)]
27pub struct LayerOrder(u16);
28
29impl LayerOrder {
30    /// The order of the root layer.
31    pub const fn root() -> Self {
32        Self(std::u16::MAX - 1)
33    }
34
35    /// The order of the style attribute layer.
36    pub const fn style_attribute() -> Self {
37        Self(std::u16::MAX)
38    }
39
40    /// Returns whether this layer is for the style attribute, which behaves
41    /// differently in terms of !important, see
42    /// https://github.com/w3c/csswg-drafts/issues/6872
43    ///
44    /// (This is a bit silly, mind-you, but it's needed so that revert-layer
45    /// behaves correctly).
46    #[inline]
47    pub fn is_style_attribute_layer(&self) -> bool {
48        *self == Self::style_attribute()
49    }
50
51    /// The first cascade layer order.
52    pub const fn first() -> Self {
53        Self(0)
54    }
55
56    /// Increment the cascade layer order.
57    #[inline]
58    pub fn inc(&mut self) {
59        if self.0 != std::u16::MAX - 1 {
60            self.0 += 1;
61        }
62    }
63}
64
65/// A `<layer-name>`: https://drafts.csswg.org/css-cascade-5/#typedef-layer-name
66#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
67pub struct LayerName(pub SmallVec<[AtomIdent; 1]>);
68
69impl LayerName {
70    /// Returns an empty layer name (which isn't a valid final state, so caller
71    /// is responsible to fill up the name before use).
72    pub fn new_empty() -> Self {
73        Self(Default::default())
74    }
75
76    /// Returns a synthesized name for an anonymous layer.
77    pub fn new_anonymous() -> Self {
78        use std::sync::atomic::{AtomicUsize, Ordering};
79        static NEXT_ANONYMOUS_LAYER_NAME: AtomicUsize = AtomicUsize::new(0);
80
81        let mut name = SmallVec::new();
82        let next_id = NEXT_ANONYMOUS_LAYER_NAME.fetch_add(1, Ordering::Relaxed);
83        // The parens don't _technically_ prevent conflicts with authors, as
84        // authors could write escaped parens as part of the identifier, I
85        // think, but highly reduces the possibility.
86        name.push(AtomIdent::from(&*format!("-moz-anon-layer({})", next_id)));
87
88        LayerName(name)
89    }
90
91    /// Returns the names of the layers. That is, for a layer like `foo.bar`,
92    /// it'd return [foo, bar].
93    pub fn layer_names(&self) -> &[AtomIdent] {
94        &self.0
95    }
96}
97
98impl Parse for LayerName {
99    fn parse<'i, 't>(
100        _: &ParserContext,
101        input: &mut Parser<'i, 't>,
102    ) -> Result<Self, ParseError<'i>> {
103        let mut result = SmallVec::new();
104        result.push(AtomIdent::from(&**input.expect_ident()?));
105        loop {
106            let next_name = input.try_parse(|input| -> Result<AtomIdent, ParseError<'i>> {
107                match input.next_including_whitespace()? {
108                    Token::Delim('.') => {},
109                    other => {
110                        let t = other.clone();
111                        return Err(input.new_unexpected_token_error(t));
112                    },
113                }
114
115                let name = match input.next_including_whitespace()? {
116                    Token::Ident(ref ident) => ident,
117                    other => {
118                        let t = other.clone();
119                        return Err(input.new_unexpected_token_error(t));
120                    },
121                };
122
123                Ok(AtomIdent::from(&**name))
124            });
125
126            match next_name {
127                Ok(name) => result.push(name),
128                Err(..) => break,
129            }
130        }
131        Ok(LayerName(result))
132    }
133}
134
135impl ToCss for LayerName {
136    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
137    where
138        W: Write,
139    {
140        let mut first = true;
141        for name in self.0.iter() {
142            if !first {
143                dest.write_char('.')?;
144            }
145            first = false;
146            name.to_css(dest)?;
147        }
148        Ok(())
149    }
150}
151
152#[derive(Debug, ToShmem)]
153/// A block `@layer <name>? { ... }`
154/// https://drafts.csswg.org/css-cascade-5/#layer-block
155pub struct LayerBlockRule {
156    /// The layer name, or `None` if anonymous.
157    pub name: Option<LayerName>,
158    /// The nested rules.
159    pub rules: Arc<Locked<CssRules>>,
160    /// The source position where this rule was found.
161    pub source_location: SourceLocation,
162}
163
164impl ToCssWithGuard for LayerBlockRule {
165    fn to_css(
166        &self,
167        guard: &SharedRwLockReadGuard,
168        dest: &mut style_traits::CssStringWriter,
169    ) -> fmt::Result {
170        dest.write_str("@layer")?;
171        if let Some(ref name) = self.name {
172            dest.write_char(' ')?;
173            name.to_css(&mut CssWriter::new(dest))?;
174        }
175        self.rules.read_with(guard).to_css_block(guard, dest)
176    }
177}
178
179impl DeepCloneWithLock for LayerBlockRule {
180    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
181        Self {
182            name: self.name.clone(),
183            rules: Arc::new(
184                lock.wrap(
185                    self.rules
186                        .read_with(guard)
187                        .deep_clone_with_lock(lock, guard),
188                ),
189            ),
190            source_location: self.source_location.clone(),
191        }
192    }
193}
194
195/// A statement `@layer <name>, <name>, <name>;`
196///
197/// https://drafts.csswg.org/css-cascade-5/#layer-empty
198#[derive(Clone, Debug, ToShmem)]
199pub struct LayerStatementRule {
200    /// The list of layers to sort.
201    pub names: Vec<LayerName>,
202    /// The source position where this rule was found.
203    pub source_location: SourceLocation,
204}
205
206impl ToCssWithGuard for LayerStatementRule {
207    fn to_css(
208        &self,
209        _: &SharedRwLockReadGuard,
210        dest: &mut style_traits::CssStringWriter,
211    ) -> fmt::Result {
212        let mut writer = CssWriter::new(dest);
213        writer.write_str("@layer ")?;
214        let mut first = true;
215        for name in &*self.names {
216            if !first {
217                writer.write_str(", ")?;
218            }
219            first = false;
220            name.to_css(&mut writer)?;
221        }
222        writer.write_char(';')
223    }
224}