/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! [CSS cascade origins](https://drafts.csswg.org/css-cascade/#cascading-origins). use std::marker::PhantomData; use std::ops::BitOrAssign; /// Each style rule has an origin, which determines where it enters the cascade. /// /// #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem, PartialOrd, Ord)] #[repr(u8)] pub enum Origin { /// UserAgent = 0x1, /// User = 0x2, /// Author = 0x4, } impl Origin { /// Returns an origin that goes in order for `index`. /// /// This is used for iterating across origins. fn from_index(index: i8) -> Option { Some(match index { 0 => Origin::Author, 1 => Origin::User, 2 => Origin::UserAgent, _ => return None, }) } fn to_index(self) -> i8 { match self { Origin::Author => 0, Origin::User => 1, Origin::UserAgent => 2, } } /// Returns an iterator from this origin, towards all the less specific /// origins. So for `UserAgent`, it'd iterate through all origins. #[inline] pub fn following_including(self) -> OriginSetIterator { OriginSetIterator { set: OriginSet::ORIGIN_USER | OriginSet::ORIGIN_AUTHOR | OriginSet::ORIGIN_USER_AGENT, cur: self.to_index(), rev: true, } } } bitflags! { /// A set of origins. This is equivalent to Gecko's OriginFlags. #[derive(MallocSizeOf)] pub struct OriginSet: u8 { /// const ORIGIN_USER_AGENT = Origin::UserAgent as u8; /// const ORIGIN_USER = Origin::User as u8; /// const ORIGIN_AUTHOR = Origin::Author as u8; } } impl OriginSet { /// Returns an iterator over the origins present in this `OriginSet`. /// /// See the `OriginSet` documentation for information about the order /// origins are iterated. pub fn iter(&self) -> OriginSetIterator { OriginSetIterator { set: *self, cur: 0, rev: false, } } } impl From for OriginSet { fn from(origin: Origin) -> Self { Self::from_bits_truncate(origin as u8) } } impl BitOrAssign for OriginSet { fn bitor_assign(&mut self, origin: Origin) { *self |= OriginSet::from(origin); } } /// Iterates over the origins present in an `OriginSet`, in order from /// highest priority (author) to lower (user agent). #[derive(Clone)] pub struct OriginSetIterator { set: OriginSet, cur: i8, rev: bool, } impl Iterator for OriginSetIterator { type Item = Origin; fn next(&mut self) -> Option { loop { let origin = Origin::from_index(self.cur)?; if self.rev { self.cur -= 1; } else { self.cur += 1; } if self.set.contains(origin.into()) { return Some(origin); } } } } /// An object that stores a `T` for each origin of the CSS cascade. #[derive(Debug, Default, MallocSizeOf)] pub struct PerOrigin { /// Data for `Origin::UserAgent`. pub user_agent: T, /// Data for `Origin::User`. pub user: T, /// Data for `Origin::Author`. pub author: T, } impl PerOrigin { /// Returns a reference to the per-origin data for the specified origin. #[inline] pub fn borrow_for_origin(&self, origin: &Origin) -> &T { match *origin { Origin::UserAgent => &self.user_agent, Origin::User => &self.user, Origin::Author => &self.author, } } /// Returns a mutable reference to the per-origin data for the specified /// origin. #[inline] pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut T { match *origin { Origin::UserAgent => &mut self.user_agent, Origin::User => &mut self.user, Origin::Author => &mut self.author, } } /// Iterates over references to per-origin extra style data, from highest /// level (author) to lowest (user agent). pub fn iter_origins(&self) -> PerOriginIter { PerOriginIter { data: &self, cur: 0, rev: false, } } /// Iterates over references to per-origin extra style data, from lowest /// level (user agent) to highest (author). pub fn iter_origins_rev(&self) -> PerOriginIter { PerOriginIter { data: &self, cur: 2, rev: true, } } /// Iterates over mutable references to per-origin extra style data, from /// highest level (author) to lowest (user agent). pub fn iter_mut_origins(&mut self) -> PerOriginIterMut { PerOriginIterMut { data: self, cur: 0, _marker: PhantomData, } } } /// Iterator over `PerOrigin`, from highest level (author) to lowest /// (user agent). /// /// We rely on this specific order for correctly looking up @font-face, /// @counter-style and @keyframes rules. pub struct PerOriginIter<'a, T: 'a> { data: &'a PerOrigin, cur: i8, rev: bool, } impl<'a, T> Iterator for PerOriginIter<'a, T> where T: 'a, { type Item = (&'a T, Origin); fn next(&mut self) -> Option { let origin = Origin::from_index(self.cur)?; self.cur += if self.rev { -1 } else { 1 }; Some((self.data.borrow_for_origin(&origin), origin)) } } /// Like `PerOriginIter`, but iterates over mutable references to the /// per-origin data. /// /// We must use unsafe code here since it's not possible for the borrow /// checker to know that we are safely returning a different reference /// each time from `next()`. pub struct PerOriginIterMut<'a, T: 'a> { data: *mut PerOrigin, cur: i8, _marker: PhantomData<&'a mut PerOrigin>, } impl<'a, T> Iterator for PerOriginIterMut<'a, T> where T: 'a, { type Item = (&'a mut T, Origin); fn next(&mut self) -> Option { let origin = Origin::from_index(self.cur)?; self.cur += 1; Some(( unsafe { (*self.data).borrow_mut_for_origin(&origin) }, origin, )) } }