diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs index ccf0bb11e51..03440e31927 100644 --- a/components/style/stylesheets/import_rule.rs +++ b/components/style/stylesheets/import_rule.rs @@ -10,6 +10,7 @@ use crate::media_queries::MediaList; use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock}; use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; use crate::str::CssStringWriter; +use crate::stylesheets::supports_rule::SupportsCondition; use crate::stylesheets::layer_rule::LayerName; use crate::stylesheets::{CssRule, StylesheetInDocument}; use crate::values::CssUrl; @@ -24,9 +25,13 @@ use to_shmem::{self, SharedMemoryBuilder, ToShmem}; pub enum ImportSheet { /// A bonafide stylesheet. Sheet(crate::gecko::data::GeckoStyleSheet), + /// An @import created while parsing off-main-thread, whose Gecko sheet has /// yet to be created and attached. Pending, + + /// An @import created with a false , so will never be fetched. + Refused, } #[cfg(feature = "gecko")] @@ -41,6 +46,11 @@ impl ImportSheet { ImportSheet::Pending } + /// Creates a refused ImportSheet for a load that will not happen. + pub fn new_refused() -> Self { + ImportSheet::Refused + } + /// Returns a reference to the GeckoStyleSheet in this ImportSheet, if it /// exists. pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> { @@ -52,6 +62,7 @@ impl ImportSheet { } Some(s) }, + ImportSheet::Refused | ImportSheet::Pending => None, } } @@ -88,6 +99,7 @@ impl DeepCloneWithLock for ImportSheet { ImportSheet::Sheet(unsafe { GeckoStyleSheet::from_addrefed(clone) }) }, ImportSheet::Pending => ImportSheet::Pending, + ImportSheet::Refused => ImportSheet::Refused, } } } @@ -131,6 +143,16 @@ pub struct ImportLayer { pub name: Option, } +/// The supports condition in an import rule. +#[derive(Debug, Clone)] +pub struct ImportSupportsCondition { + /// The supports condition. + pub condition: SupportsCondition, + + /// If the import is enabled, from the result of the import condition. + pub enabled: bool +} + impl ToCss for ImportLayer { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where @@ -160,6 +182,9 @@ pub struct ImportRule { /// ImportSheet just has stub behavior until it appears. pub stylesheet: ImportSheet, + /// A for the rule. + pub supports: Option, + /// A `layer()` function name. pub layer: Option, @@ -185,6 +210,7 @@ impl DeepCloneWithLock for ImportRule { ImportRule { url: self.url.clone(), stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard, params), + supports: self.supports.clone(), layer: self.layer.clone(), source_location: self.source_location.clone(), } @@ -196,6 +222,12 @@ impl ToCssWithGuard for ImportRule { dest.write_str("@import ")?; self.url.to_css(&mut CssWriter::new(dest))?; + if let Some(ref supports) = self.supports { + dest.write_str(" supports(")?; + supports.condition.to_css(&mut CssWriter::new(dest))?; + dest.write_char(')')?; + } + if let Some(media) = self.stylesheet.media(guard) { if !media.is_empty() { dest.write_char(' ')?; diff --git a/components/style/stylesheets/loader.rs b/components/style/stylesheets/loader.rs index 45f7d22a55c..a8d569ad7a4 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::{ImportLayer, ImportRule}; +use crate::stylesheets::import_rule::{ImportLayer, ImportSupportsCondition, ImportRule}; use crate::values::CssUrl; use cssparser::SourceLocation; use servo_arc::Arc; @@ -25,6 +25,7 @@ pub trait StylesheetLoader { context: &ParserContext, lock: &SharedRwLock, media: Arc>, + supports: Option, layer: Option, ) -> Arc>; } diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 1fd2d20aa4d..894861b3099 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -16,7 +16,7 @@ use crate::str::starts_with_ignore_ascii_case; use crate::stylesheets::container_rule::{ContainerCondition, ContainerRule}; use crate::stylesheets::document_rule::DocumentCondition; use crate::stylesheets::font_feature_values_rule::parse_family_name_list; -use crate::stylesheets::import_rule::ImportLayer; +use crate::stylesheets::import_rule::{ImportLayer, ImportSupportsCondition}; use crate::stylesheets::keyframes_rule::parse_keyframe_list; use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule}; use crate::stylesheets::stylesheet::Namespaces; @@ -204,7 +204,7 @@ pub enum AtRulePrelude { /// A @document rule, with its conditional. Document(DocumentCondition), /// A @import rule prelude. - Import(CssUrl, Arc>, Option), + Import(CssUrl, Arc>, Option, Option), /// A @namespace rule prelude. Namespace(Option, Namespace), /// A @layer rule prelude. @@ -241,6 +241,24 @@ 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 supports = if !static_prefs::pref!("layout.css.import-supports.enabled") { + None + } else { + input.try_parse(SupportsCondition::parse_for_import).map(|condition| { + let eval_context = ParserContext::new_with_rule_type( + &self.context, + CssRuleType::Style, + self.namespaces, + ); + + let enabled = condition.eval(&eval_context, self.namespaces); + ImportSupportsCondition { + condition, + enabled + } + }).ok() + }; + #[cfg(feature = "gecko")] let layers_enabled = static_prefs::pref!("layout.css.cascade-layers.enabled"); #[cfg(feature = "servo")] @@ -266,7 +284,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { let media = MediaList::parse(&self.context, input); let media = Arc::new(self.shared_lock.wrap(media)); - return Ok(AtRulePrelude::Import(url, media, layer)); + return Ok(AtRulePrelude::Import(url, media, supports, layer)); }, "namespace" => { if !self.check_state(State::Namespaces) { @@ -335,7 +353,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { start: &ParserState, ) -> Result { let rule = match prelude { - AtRulePrelude::Import(url, media, layer) => { + AtRulePrelude::Import(url, media, supports, layer) => { let loader = self .loader .expect("Expected a stylesheet loader for @import"); @@ -346,6 +364,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { &self.context, &self.shared_lock, media, + supports, layer, ); diff --git a/components/style/stylesheets/supports_rule.rs b/components/style/stylesheets/supports_rule.rs index 653a25ccc60..be61058fcf1 100644 --- a/components/style/stylesheets/supports_rule.rs +++ b/components/style/stylesheets/supports_rule.rs @@ -204,8 +204,8 @@ impl SupportsCondition { Token::ParenthesisBlock => { let nested = input .try_parse(|input| input.parse_nested_block(parse_condition_or_declaration)); - if nested.is_ok() { - return nested; + if let Ok(nested) = nested { + return Ok(Self::Parenthesized(Box::new(nested))); } }, Token::Function(ref ident) => { @@ -286,7 +286,7 @@ pub fn parse_condition_or_declaration<'i, 't>( input: &mut Parser<'i, 't>, ) -> Result> { if let Ok(condition) = input.try_parse(SupportsCondition::parse) { - Ok(SupportsCondition::Parenthesized(Box::new(condition))) + Ok(condition) } else { Declaration::parse(input).map(SupportsCondition::Declaration) } @@ -330,9 +330,7 @@ impl ToCss for SupportsCondition { Ok(()) }, SupportsCondition::Declaration(ref decl) => { - dest.write_char('(')?; - decl.to_css(dest)?; - dest.write_char(')') + decl.to_css(dest) }, SupportsCondition::Selector(ref selector) => { dest.write_str("selector(")?;