diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2023-05-27 06:09:24 +0200 |
---|---|---|
committer | Oriol Brufau <obrufau@igalia.com> | 2023-05-30 23:09:43 +0200 |
commit | 6bc198b7572c86d1592f279a703c89b4ce7e11a9 (patch) | |
tree | 17021e73d07414f87145681dcbb06df242ad2fcb | |
parent | 3dc3fb9412d23769ab05e0cdb13f2f11b1fe1f70 (diff) | |
download | servo-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.rs | 57 | ||||
-rw-r--r-- | components/style/stylesheets/loader.rs | 3 | ||||
-rw-r--r-- | components/style/stylesheets/rule_parser.rs | 27 | ||||
-rw-r--r-- | components/style/stylist.rs | 36 |
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, ¤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, .. } => { |