style/stylesheets/
origin.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//! [CSS cascade origins](https://drafts.csswg.org/css-cascade/#cascading-origins).
6
7use crate::derives::*;
8use std::marker::PhantomData;
9use std::ops::BitOrAssign;
10
11/// Each style rule has an origin, which determines where it enters the cascade.
12///
13/// <https://drafts.csswg.org/css-cascade/#cascading-origins>
14#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem, PartialOrd, Ord)]
15#[repr(u8)]
16pub enum Origin {
17    /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
18    UserAgent = 0x1,
19
20    /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user>
21    User = 0x2,
22
23    /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author>
24    Author = 0x4,
25}
26
27impl Origin {
28    /// Returns an origin that goes in order for `index`.
29    ///
30    /// This is used for iterating across origins.
31    fn from_index(index: i8) -> Option<Self> {
32        Some(match index {
33            0 => Origin::Author,
34            1 => Origin::User,
35            2 => Origin::UserAgent,
36            _ => return None,
37        })
38    }
39
40    fn to_index(self) -> i8 {
41        match self {
42            Origin::Author => 0,
43            Origin::User => 1,
44            Origin::UserAgent => 2,
45        }
46    }
47
48    /// Returns an iterator from this origin, towards all the less specific
49    /// origins. So for `UserAgent`, it'd iterate through all origins.
50    #[inline]
51    pub fn following_including(self) -> OriginSetIterator {
52        OriginSetIterator {
53            set: OriginSet::ORIGIN_USER | OriginSet::ORIGIN_AUTHOR | OriginSet::ORIGIN_USER_AGENT,
54            cur: self.to_index(),
55            rev: true,
56        }
57    }
58}
59
60/// A set of origins. This is equivalent to Gecko's OriginFlags.
61#[derive(Clone, Copy, PartialEq, MallocSizeOf)]
62pub struct OriginSet(u8);
63bitflags! {
64    impl OriginSet: u8 {
65        /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
66        const ORIGIN_USER_AGENT = Origin::UserAgent as u8;
67        /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user>
68        const ORIGIN_USER = Origin::User as u8;
69        /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author>
70        const ORIGIN_AUTHOR = Origin::Author as u8;
71    }
72}
73
74impl OriginSet {
75    /// Returns an iterator over the origins present in this `OriginSet`.
76    ///
77    /// See the `OriginSet` documentation for information about the order
78    /// origins are iterated.
79    pub fn iter_origins(&self) -> OriginSetIterator {
80        OriginSetIterator {
81            set: *self,
82            cur: 0,
83            rev: false,
84        }
85    }
86}
87
88impl From<Origin> for OriginSet {
89    fn from(origin: Origin) -> Self {
90        Self::from_bits_retain(origin as u8)
91    }
92}
93
94impl BitOrAssign<Origin> for OriginSet {
95    fn bitor_assign(&mut self, origin: Origin) {
96        *self |= OriginSet::from(origin);
97    }
98}
99
100/// Iterates over the origins present in an `OriginSet`, in order from
101/// highest priority (author) to lower (user agent).
102#[derive(Clone)]
103pub struct OriginSetIterator {
104    set: OriginSet,
105    cur: i8,
106    rev: bool,
107}
108
109impl Iterator for OriginSetIterator {
110    type Item = Origin;
111
112    fn next(&mut self) -> Option<Origin> {
113        loop {
114            let origin = Origin::from_index(self.cur)?;
115
116            if self.rev {
117                self.cur -= 1;
118            } else {
119                self.cur += 1;
120            }
121
122            if self.set.contains(origin.into()) {
123                return Some(origin);
124            }
125        }
126    }
127}
128
129/// An object that stores a `T` for each origin of the CSS cascade.
130#[derive(Debug, Default, MallocSizeOf)]
131pub struct PerOrigin<T> {
132    /// Data for `Origin::UserAgent`.
133    pub user_agent: T,
134
135    /// Data for `Origin::User`.
136    pub user: T,
137
138    /// Data for `Origin::Author`.
139    pub author: T,
140}
141
142impl<T> PerOrigin<T> {
143    /// Returns a reference to the per-origin data for the specified origin.
144    #[inline]
145    pub fn borrow_for_origin(&self, origin: &Origin) -> &T {
146        match *origin {
147            Origin::UserAgent => &self.user_agent,
148            Origin::User => &self.user,
149            Origin::Author => &self.author,
150        }
151    }
152
153    /// Returns a mutable reference to the per-origin data for the specified
154    /// origin.
155    #[inline]
156    pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut T {
157        match *origin {
158            Origin::UserAgent => &mut self.user_agent,
159            Origin::User => &mut self.user,
160            Origin::Author => &mut self.author,
161        }
162    }
163
164    /// Iterates over references to per-origin extra style data, from highest
165    /// level (author) to lowest (user agent).
166    pub fn iter_origins(&self) -> PerOriginIter<'_, T> {
167        PerOriginIter {
168            data: &self,
169            cur: 0,
170            rev: false,
171        }
172    }
173
174    /// Iterates over references to per-origin extra style data, from lowest
175    /// level (user agent) to highest (author).
176    pub fn iter_origins_rev(&self) -> PerOriginIter<'_, T> {
177        PerOriginIter {
178            data: &self,
179            cur: 2,
180            rev: true,
181        }
182    }
183
184    /// Iterates over mutable references to per-origin extra style data, from
185    /// highest level (author) to lowest (user agent).
186    pub fn iter_mut_origins(&mut self) -> PerOriginIterMut<'_, T> {
187        PerOriginIterMut {
188            data: self,
189            cur: 0,
190            _marker: PhantomData,
191        }
192    }
193}
194
195/// Iterator over `PerOrigin<T>`, from highest level (author) to lowest
196/// (user agent).
197///
198/// We rely on this specific order for correctly looking up @font-face,
199/// @counter-style and @keyframes rules.
200pub struct PerOriginIter<'a, T: 'a> {
201    data: &'a PerOrigin<T>,
202    cur: i8,
203    rev: bool,
204}
205
206impl<'a, T> Iterator for PerOriginIter<'a, T>
207where
208    T: 'a,
209{
210    type Item = (&'a T, Origin);
211
212    fn next(&mut self) -> Option<Self::Item> {
213        let origin = Origin::from_index(self.cur)?;
214
215        self.cur += if self.rev { -1 } else { 1 };
216
217        Some((self.data.borrow_for_origin(&origin), origin))
218    }
219}
220
221/// Like `PerOriginIter<T>`, but iterates over mutable references to the
222/// per-origin data.
223///
224/// We must use unsafe code here since it's not possible for the borrow
225/// checker to know that we are safely returning a different reference
226/// each time from `next()`.
227pub struct PerOriginIterMut<'a, T: 'a> {
228    data: *mut PerOrigin<T>,
229    cur: i8,
230    _marker: PhantomData<&'a mut PerOrigin<T>>,
231}
232
233impl<'a, T> Iterator for PerOriginIterMut<'a, T>
234where
235    T: 'a,
236{
237    type Item = (&'a mut T, Origin);
238
239    fn next(&mut self) -> Option<Self::Item> {
240        let origin = Origin::from_index(self.cur)?;
241
242        self.cur += 1;
243
244        Some((
245            unsafe { (*self.data).borrow_mut_for_origin(&origin) },
246            origin,
247        ))
248    }
249}