aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2023-05-27 06:09:24 +0200
committerOriol Brufau <obrufau@igalia.com>2023-05-30 23:09:43 +0200
commit6bc198b7572c86d1592f279a703c89b4ce7e11a9 (patch)
tree17021e73d07414f87145681dcbb06df242ad2fcb
parent3dc3fb9412d23769ab05e0cdb13f2f11b1fe1f70 (diff)
downloadservo-6bc198b7572c86d1592f279a703c89b4ce7e11a9.tar.gz
servo-6bc198b7572c86d1592f279a703c89b4ce7e11a9.zip
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
-rw-r--r--components/style/stylesheets/import_rule.rs57
-rw-r--r--components/style/stylesheets/loader.rs3
-rw-r--r--components/style/stylesheets/rule_parser.rs27
-rw-r--r--components/style/stylist.rs36
4 files changed, 98 insertions, 25 deletions
diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs
index 8f518f29bac..d60802cf0ab 100644
--- a/components/style/stylesheets/import_rule.rs
+++ b/components/style/stylesheets/import_rule.rs
@@ -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))?;
- },
- _ => {},
- };
+ }
+ }
+
+ if let Some(ref layer) = self.layer {
+ dest.write_char(' ')?;
+ layer.to_css(&mut CssWriter::new(dest))?;
+ }
- dest.write_str(";")
+ dest.write_char(';')
}
}
diff --git a/components/style/stylesheets/loader.rs b/components/style/stylesheets/loader.rs
index ce7c563db05..c145bafdc04 100644
--- a/components/style/stylesheets/loader.rs
+++ b/components/style/stylesheets/loader.rs
@@ -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>>;
}
diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs
index 7fb76a8d7a9..421431a8c10 100644
--- a/components/style/stylesheets/rule_parser.rs
+++ b/components/style/stylesheets/rule_parser.rs
@@ -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;
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index 0a87da95bc8..1c90886e17d 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -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, &current_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, .. } => {