Bug 1728348 - Add some scaffolding for @layer rules. r=boris

Not hooked anywhere yet, so this doesn't change behavior, but adds the
basic data model etc.

Adding parsing support requires some changes to cssparser to allow the
same at rule to be block and statement-like at the same time, so better
done separately.

Differential Revision: https://phabricator.services.mozilla.com/D124079
This commit is contained in:
Emilio Cobos Álvarez 2021-09-03 09:12:43 +00:00
Родитель c6ef85e3b3
Коммит 0f945d081b
6 изменённых файлов: 208 добавлений и 5 удалений

Просмотреть файл

@ -555,7 +555,7 @@ impl StylesheetInvalidationSet {
self.collect_invalidations_for_rule(rule, guard, device, quirks_mode)
},
Document(..) | Import(..) | Media(..) | Supports(..) => {
Document(..) | Import(..) | Media(..) | Supports(..) | Layer(..) => {
if !is_generic_change &&
!EffectiveRules::is_effective(guard, device, quirks_mode, rule)
{
@ -596,7 +596,7 @@ impl StylesheetInvalidationSet {
}
}
},
Document(..) | Namespace(..) | Import(..) | Media(..) | Supports(..) => {
Document(..) | Namespace(..) | Import(..) | Media(..) | Supports(..) | Layer(..) => {
// Do nothing, relevant nested rules are visited as part of the
// iteration.
},

Просмотреть файл

@ -0,0 +1,171 @@
/* 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/. */
//! A [`@layer`][layer] urle.
//!
//! [layer]: https://drafts.csswg.org/css-cascade-5/#layering
use crate::parser::{Parse, ParserContext};
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::values::AtomIdent;
use super::CssRules;
use cssparser::{Parser, SourceLocation, ToCss as CssParserToCss, Token};
use servo_arc::Arc;
use smallvec::SmallVec;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
/// A `<layer-name>`: https://drafts.csswg.org/css-cascade-5/#typedef-layer-name
#[derive(Clone, Debug, ToShmem)]
pub struct LayerName(SmallVec<[AtomIdent; 1]>);
impl Parse for LayerName {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let mut result = SmallVec::new();
result.push(AtomIdent::from(&**input.expect_ident()?));
loop {
let next_name = input.try_parse(|input| -> Result<AtomIdent, ParseError<'i>> {
match input.next_including_whitespace()? {
Token::Delim('.') => {},
other => {
let t = other.clone();
return Err(input.new_unexpected_token_error(t));
},
}
let name = match input.next_including_whitespace()? {
Token::Ident(ref ident) => ident,
other => {
let t = other.clone();
return Err(input.new_unexpected_token_error(t));
},
};
Ok(AtomIdent::from(&**name))
});
match next_name {
Ok(name) => result.push(name),
Err(..) => break,
}
}
Ok(LayerName(result))
}
}
impl ToCss for LayerName {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
let mut first = true;
for name in self.0.iter() {
if !first {
dest.write_char('.')?;
}
first = false;
name.to_css(dest)?;
}
Ok(())
}
}
/// The kind of layer rule this is.
#[derive(Debug, ToShmem)]
pub enum LayerRuleKind {
/// A block `@layer <name>? { ... }`
Block {
/// The layer name, or `None` if anonymous.
name: Option<LayerName>,
/// The nested rules.
rules: Arc<Locked<CssRules>>,
},
/// A statement `@layer <name>, <name>, <name>;`
Statement {
/// The list of layers to sort.
names: SmallVec<[LayerName; 3]>,
},
}
/// A [`@layer`][layer] urle.
///
/// [layer]: https://drafts.csswg.org/css-cascade-5/#layering
#[derive(Debug, ToShmem)]
pub struct LayerRule {
/// The kind of layer rule we are.
pub kind: LayerRuleKind,
/// The source position where this media rule was found.
pub source_location: SourceLocation,
}
impl ToCssWithGuard for LayerRule {
fn to_css(
&self,
guard: &SharedRwLockReadGuard,
dest: &mut crate::str::CssStringWriter,
) -> fmt::Result {
dest.write_str("@layer ")?;
match self.kind {
LayerRuleKind::Block {
ref name,
ref rules,
} => {
if let Some(ref name) = *name {
name.to_css(&mut CssWriter::new(dest))?;
dest.write_char(' ')?;
}
rules.read_with(guard).to_css_block(guard, dest)
},
LayerRuleKind::Statement { ref names } => {
let mut writer = CssWriter::new(dest);
let mut first = true;
for name in &**names {
if !first {
writer.write_str(", ")?;
}
first = false;
name.to_css(&mut writer)?;
}
dest.write_char(';')
},
}
}
}
impl DeepCloneWithLock for LayerRule {
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
params: &DeepCloneParams,
) -> Self {
Self {
kind: match self.kind {
LayerRuleKind::Block {
ref name,
ref rules,
} => LayerRuleKind::Block {
name: name.clone(),
rules: Arc::new(
lock.wrap(
rules
.read_with(guard)
.deep_clone_with_lock(lock, guard, params),
),
),
},
LayerRuleKind::Statement { ref names } => LayerRuleKind::Statement {
names: names.clone(),
},
},
source_location: self.source_location.clone(),
}
}
}

Просмотреть файл

@ -11,6 +11,7 @@ mod font_face_rule;
pub mod font_feature_values_rule;
pub mod import_rule;
pub mod keyframes_rule;
mod layer_rule;
mod loader;
mod media_rule;
mod namespace_rule;
@ -49,6 +50,7 @@ pub use self::font_face_rule::FontFaceRule;
pub use self::font_feature_values_rule::FontFeatureValuesRule;
pub use self::import_rule::ImportRule;
pub use self::keyframes_rule::KeyframesRule;
pub use self::layer_rule::LayerRule;
pub use self::loader::StylesheetLoader;
pub use self::media_rule::MediaRule;
pub use self::namespace_rule::NamespaceRule;
@ -257,6 +259,7 @@ pub enum CssRule {
Supports(Arc<Locked<SupportsRule>>),
Page(Arc<Locked<PageRule>>),
Document(Arc<Locked<DocumentRule>>),
Layer(Arc<Locked<LayerRule>>),
}
impl CssRule {
@ -297,11 +300,14 @@ impl CssRule {
CssRule::Document(ref lock) => {
lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
},
// TODO(emilio): Add memory reporting for @layer rules.
CssRule::Layer(_) => 0,
}
}
}
#[allow(missing_docs)]
/// https://drafts.csswg.org/cssom-1/#dom-cssrule-type
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
pub enum CssRuleType {
// https://drafts.csswg.org/cssom/#the-cssrule-interface
@ -323,10 +329,13 @@ pub enum CssRuleType {
Supports = 12,
// https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface
Document = 13,
// https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues
// https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues
FontFeatureValues = 14,
// https://drafts.csswg.org/css-device-adapt/#css-rule-interface
Viewport = 15,
// After viewport, all rules should return 0 from the API, but we still need
// a constant somewhere.
Layer = 16,
}
#[allow(missing_docs)]
@ -353,6 +362,7 @@ impl CssRule {
CssRule::Supports(_) => CssRuleType::Supports,
CssRule::Page(_) => CssRuleType::Page,
CssRule::Document(_) => CssRuleType::Document,
CssRule::Layer(_) => CssRuleType::Layer,
}
}
@ -361,6 +371,8 @@ impl CssRule {
// CssRule::Charset(..) => State::Start,
CssRule::Import(..) => State::Imports,
CssRule::Namespace(..) => State::Namespaces,
// TODO(emilio): We'll need something here for non-block layer
// rules.
_ => State::Body,
}
}
@ -485,6 +497,12 @@ impl DeepCloneWithLock for CssRule {
lock.wrap(rule.deep_clone_with_lock(lock, guard, params)),
))
},
CssRule::Layer(ref arc) => {
let rule = arc.read_with(guard);
CssRule::Layer(Arc::new(
lock.wrap(rule.deep_clone_with_lock(lock, guard, params)),
))
}
}
}
}
@ -505,6 +523,7 @@ impl ToCssWithGuard for CssRule {
CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::Layer(ref lock) => lock.read_with(guard).to_css(guard, dest),
}
}
}

Просмотреть файл

@ -105,6 +105,15 @@ where
}
Some(supports_rule.rules.read_with(guard).0.iter())
},
CssRule::Layer(ref lock) => {
use crate::stylesheets::layer_rule::LayerRuleKind;
let layer_rule = lock.read_with(guard);
match layer_rule.kind {
LayerRuleKind::Block { ref rules, .. } => Some(rules.read_with(guard).0.iter()),
LayerRuleKind::Statement { .. } => None,
}
}
}
}

Просмотреть файл

@ -367,7 +367,10 @@ impl SanitizationKind {
CssRule::Document(..) |
CssRule::Media(..) |
CssRule::Supports(..) |
CssRule::Import(..) => false,
CssRule::Import(..) |
// TODO(emilio): Perhaps Layer should not be always sanitized? But
// we sanitize @media and co, so this seems safer for now.
CssRule::Layer(..) => false,
CssRule::FontFace(..) | CssRule::Namespace(..) | CssRule::Style(..) => true,

Просмотреть файл

@ -2373,6 +2373,7 @@ impl CascadeData {
CssRule::Page(..) |
CssRule::Viewport(..) |
CssRule::Document(..) |
CssRule::Layer(..) |
CssRule::FontFeatureValues(..) => {
// Not affected by device changes.
continue;