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