mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
This patch looks bigger than it is, but it's mostly because of plumbing. To implement revert-layer we need not only the cascade origin of the declaration, but the whole cascade level, plus also the layer order. In order to do this, encapsulate these two things inside a 32-bit `CascadePriority` struct and plumb it through the rule tree and so on. This allows us to remove the packing and unpacking of CascadeLevel, though I kept the ShadowCascadeOrder limit for now in case we need to reintroduce it. Fix `!important` behavior of layers while at it (implementing it in `CascadeLevel::cmp`, spec quote included since it was tricky to find) since some revert-layer tests were depending on it. The style attribute test is failing now, but follow-up commit fixes it, see spec issue. In terms of the actual keyword implementation, it's sort of straight-forward: We implement revert and revert-layer in a shared way, by storing the cascade priority that reverted it. Differential Revision: https://phabricator.services.mozilla.com/D133372
247 lines
6.8 KiB
Rust
247 lines
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, PartialOrd, Ord)]
|
|
#[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,
|
|
))
|
|
}
|
|
}
|