зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
c6ef85e3b3
Коммит
0f945d081b
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче