gecko-dev/servo/components/style/stylesheets/origin.rs

248 строки
6.8 KiB
Rust

/* 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.
///
/// <https://drafts.csswg.org/css-cascade/#cascading-origins>
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
#[repr(u8)]
pub enum Origin {
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
UserAgent = 0x1,
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-user>
User = 0x2,
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-author>
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<Self> {
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 {
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
const ORIGIN_USER_AGENT = Origin::UserAgent as u8;
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-user>
const ORIGIN_USER = Origin::User as u8;
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-author>
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<Origin> for OriginSet {
fn from(origin: Origin) -> Self {
Self::from_bits_truncate(origin as u8)
}
}
impl BitOrAssign<Origin> 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<Origin> {
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<T> {
/// Data for `Origin::UserAgent`.
pub user_agent: T,
/// Data for `Origin::User`.
pub user: T,
/// Data for `Origin::Author`.
pub author: T,
}
impl<T> PerOrigin<T> {
/// 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<T> {
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<T> {
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<T> {
PerOriginIterMut {
data: self,
cur: 0,
_marker: PhantomData,
}
}
}
/// Iterator over `PerOrigin<T>`, 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<T>,
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<Self::Item> {
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<T>`, 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<T>,
cur: i8,
_marker: PhantomData<&'a mut PerOrigin<T>>,
}
impl<'a, T> Iterator for PerOriginIterMut<'a, T>
where
T: 'a,
{
type Item = (&'a mut T, Origin);
fn next(&mut self) -> Option<Self::Item> {
let origin = Origin::from_index(self.cur)?;
self.cur += 1;
Some((
unsafe { (*self.data).borrow_mut_for_origin(&origin) },
origin,
))
}
}