mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
style: Implement @import layer|layer(<name>)
This works modulo the existing nested layer order bug. Will be covered by WPT /css/css-cascade/layer-import.html once the feature is enabled (I can probably enable it right away for those tests, but I'd rather fix the obvious bugs first). Differential Revision: https://phabricator.services.mozilla.com/D124538
This commit is contained in:
parent
3dc3fb9412
commit
6bc198b757
4 changed files with 98 additions and 25 deletions
|
@ -11,6 +11,7 @@ use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock};
|
|||
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
|
||||
use crate::str::CssStringWriter;
|
||||
use crate::stylesheets::{CssRule, StylesheetInDocument};
|
||||
use crate::stylesheets::layer_rule::LayerName;
|
||||
use crate::values::CssUrl;
|
||||
use cssparser::SourceLocation;
|
||||
use std::fmt::{self, Write};
|
||||
|
@ -135,6 +136,31 @@ impl DeepCloneWithLock for ImportSheet {
|
|||
}
|
||||
}
|
||||
|
||||
/// The layer keyword or function in an import rule.
|
||||
#[derive(Debug)]
|
||||
pub struct ImportLayer {
|
||||
/// Whether the layer is anonymous.
|
||||
pub is_anonymous: bool,
|
||||
/// The layer name.
|
||||
pub name: LayerName,
|
||||
}
|
||||
|
||||
|
||||
impl ToCss for ImportLayer {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
if self.is_anonymous {
|
||||
dest.write_str("layer")
|
||||
} else {
|
||||
dest.write_str("layer(")?;
|
||||
self.name.to_css(dest)?;
|
||||
dest.write_char(')')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`@import`][import] at-rule.
|
||||
///
|
||||
/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
|
||||
|
@ -148,6 +174,9 @@ pub struct ImportRule {
|
|||
/// ImportSheet just has stub behavior until it appears.
|
||||
pub stylesheet: ImportSheet,
|
||||
|
||||
/// A `layer()` function name.
|
||||
pub layer: Option<ImportLayer>,
|
||||
|
||||
/// The line and column of the rule's source code.
|
||||
pub source_location: SourceLocation,
|
||||
}
|
||||
|
@ -170,6 +199,16 @@ impl DeepCloneWithLock for ImportRule {
|
|||
ImportRule {
|
||||
url: self.url.clone(),
|
||||
stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard, params),
|
||||
layer: self.layer.as_ref().map(|layer| {
|
||||
ImportLayer {
|
||||
is_anonymous: layer.is_anonymous,
|
||||
name: if layer.is_anonymous {
|
||||
LayerName::new_anonymous()
|
||||
} else {
|
||||
layer.name.clone()
|
||||
},
|
||||
}
|
||||
}),
|
||||
source_location: self.source_location.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -180,14 +219,18 @@ impl ToCssWithGuard for ImportRule {
|
|||
dest.write_str("@import ")?;
|
||||
self.url.to_css(&mut CssWriter::new(dest))?;
|
||||
|
||||
match self.stylesheet.media(guard) {
|
||||
Some(media) if !media.is_empty() => {
|
||||
dest.write_str(" ")?;
|
||||
if let Some(media) = self.stylesheet.media(guard) {
|
||||
if !media.is_empty() {
|
||||
dest.write_char(' ')?;
|
||||
media.to_css(&mut CssWriter::new(dest))?;
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
dest.write_str(";")
|
||||
if let Some(ref layer) = self.layer {
|
||||
dest.write_char(' ')?;
|
||||
layer.to_css(&mut CssWriter::new(dest))?;
|
||||
}
|
||||
|
||||
dest.write_char(';')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
use crate::media_queries::MediaList;
|
||||
use crate::parser::ParserContext;
|
||||
use crate::shared_lock::{Locked, SharedRwLock};
|
||||
use crate::stylesheets::import_rule::ImportRule;
|
||||
use crate::stylesheets::import_rule::{ImportRule, ImportLayer};
|
||||
use crate::values::CssUrl;
|
||||
use cssparser::SourceLocation;
|
||||
use servo_arc::Arc;
|
||||
|
@ -25,5 +25,6 @@ pub trait StylesheetLoader {
|
|||
context: &ParserContext,
|
||||
lock: &SharedRwLock,
|
||||
media: Arc<Locked<MediaList>>,
|
||||
layer: Option<ImportLayer>,
|
||||
) -> Arc<Locked<ImportRule>>;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
|
|||
use crate::stylesheets::keyframes_rule::parse_keyframe_list;
|
||||
use crate::stylesheets::stylesheet::Namespaces;
|
||||
use crate::stylesheets::supports_rule::SupportsCondition;
|
||||
use crate::stylesheets::import_rule::ImportLayer;
|
||||
use crate::stylesheets::layer_rule::{LayerName, LayerRuleKind};
|
||||
use crate::stylesheets::viewport_rule;
|
||||
use crate::stylesheets::AllowImportRules;
|
||||
|
@ -169,7 +170,7 @@ pub enum AtRulePrelude {
|
|||
/// A @document rule, with its conditional.
|
||||
Document(DocumentCondition),
|
||||
/// A @import rule prelude.
|
||||
Import(CssUrl, Arc<Locked<MediaList>>),
|
||||
Import(CssUrl, Arc<Locked<MediaList>>, Option<ImportLayer>),
|
||||
/// A @namespace rule prelude.
|
||||
Namespace(Option<Prefix>, Namespace),
|
||||
/// A @layer rule prelude.
|
||||
|
@ -206,10 +207,29 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
|||
let url_string = input.expect_url_or_string()?.as_ref().to_owned();
|
||||
let url = CssUrl::parse_from_string(url_string, &self.context, CorsMode::None);
|
||||
|
||||
let layer = if !static_prefs::pref!("layout.css.cascade-layers.enabled") {
|
||||
None
|
||||
} else if input.try_parse(|input| input.expect_ident_matching("layer")).is_ok() {
|
||||
Some(ImportLayer {
|
||||
is_anonymous: true,
|
||||
name: LayerName::new_anonymous(),
|
||||
})
|
||||
} else {
|
||||
input.try_parse(|input| {
|
||||
input.expect_function_matching("layer")?;
|
||||
input.parse_nested_block(|input| {
|
||||
LayerName::parse(&self.context, input)
|
||||
}).map(|name| ImportLayer {
|
||||
is_anonymous: false,
|
||||
name,
|
||||
})
|
||||
}).ok()
|
||||
};
|
||||
|
||||
let media = MediaList::parse(&self.context, input);
|
||||
let media = Arc::new(self.shared_lock.wrap(media));
|
||||
|
||||
return Ok(AtRulePrelude::Import(url, media));
|
||||
return Ok(AtRulePrelude::Import(url, media, layer));
|
||||
},
|
||||
"namespace" => {
|
||||
if !self.check_state(State::Namespaces) {
|
||||
|
@ -263,7 +283,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
|||
start: &ParserState,
|
||||
) -> Result<Self::AtRule, ()> {
|
||||
let rule = match prelude {
|
||||
AtRulePrelude::Import(url, media) => {
|
||||
AtRulePrelude::Import(url, media, layer) => {
|
||||
let loader = self
|
||||
.loader
|
||||
.expect("Expected a stylesheet loader for @import");
|
||||
|
@ -274,6 +294,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
|||
&self.context,
|
||||
&self.shared_lock,
|
||||
media,
|
||||
layer,
|
||||
);
|
||||
|
||||
self.state = State::Imports;
|
||||
|
|
|
@ -2329,15 +2329,36 @@ impl CascadeData {
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
fn maybe_register_layer(data: &mut CascadeData, layer: &LayerName) -> u32 {
|
||||
// TODO: Measure what's more common / expensive, if
|
||||
// layer.clone() or the double hash lookup in the insert
|
||||
// case.
|
||||
if let Some(order) = data.layer_order.get(layer) {
|
||||
return *order;
|
||||
}
|
||||
let order = data.next_layer_order;
|
||||
data.layer_order.insert(layer.clone(), order);
|
||||
data.next_layer_order += 1;
|
||||
order
|
||||
}
|
||||
|
||||
let mut layer_names_to_pop = 0;
|
||||
let mut children_layer_order = current_layer_order;
|
||||
match *rule {
|
||||
CssRule::Import(ref lock) => {
|
||||
let import_rule = lock.read_with(guard);
|
||||
if rebuild_kind.should_rebuild_invalidation() {
|
||||
let import_rule = lock.read_with(guard);
|
||||
self.effective_media_query_results
|
||||
.saw_effective(import_rule);
|
||||
}
|
||||
if let Some(ref layer) = import_rule.layer {
|
||||
for name in layer.name.layer_names() {
|
||||
current_layer.0.push(name.clone());
|
||||
children_layer_order = maybe_register_layer(self, ¤t_layer);
|
||||
layer_names_to_pop += 1;
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
CssRule::Media(ref lock) => {
|
||||
|
@ -2349,19 +2370,6 @@ impl CascadeData {
|
|||
CssRule::Layer(ref lock) => {
|
||||
use crate::stylesheets::layer_rule::LayerRuleKind;
|
||||
|
||||
fn maybe_register_layer(data: &mut CascadeData, layer: &LayerName) -> u32 {
|
||||
// TODO: Measure what's more common / expensive, if
|
||||
// layer.clone() or the double hash lookup in the insert
|
||||
// case.
|
||||
if let Some(order) = data.layer_order.get(layer) {
|
||||
return *order;
|
||||
}
|
||||
let order = data.next_layer_order;
|
||||
data.layer_order.insert(layer.clone(), order);
|
||||
data.next_layer_order += 1;
|
||||
order
|
||||
}
|
||||
|
||||
let layer_rule = lock.read_with(guard);
|
||||
match layer_rule.kind {
|
||||
LayerRuleKind::Block { ref name, .. } => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue