diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs index cabae61d563..6e347c59ce7 100644 --- a/components/style/gecko/data.rs +++ b/components/style/gecko/data.rs @@ -25,6 +25,16 @@ use std::fmt; #[derive(Eq, PartialEq)] pub struct GeckoStyleSheet(*const DomStyleSheet); +// NOTE(emilio): These are kind of a lie. We allow to make these Send + Sync so that other data +// structures can also be Send and Sync, but Gecko's stylesheets are main-thread-reference-counted. +// +// We assert that we reference-count in the right thread (in the Addref/Release implementations). +// Sending these to a different thread can't really happen (it could theoretically really happen if +// we allowed @import rules inside a nested style rule, but that can't happen per spec and would be +// a parser bug, caught by the asserts). +unsafe impl Send for GeckoStyleSheet {} +unsafe impl Sync for GeckoStyleSheet {} + impl fmt::Debug for GeckoStyleSheet { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let contents = self.contents(); diff --git a/components/style/stylesheets/rule_list.rs b/components/style/stylesheets/rule_list.rs index c246d7ae6bb..196f8eaf62a 100644 --- a/components/style/stylesheets/rule_list.rs +++ b/components/style/stylesheets/rule_list.rs @@ -102,6 +102,15 @@ impl CssRules { dest: &mut CssStringWriter, ) -> fmt::Result { dest.write_str(" {")?; + self.to_css_block_without_opening(guard, dest) + } + + /// As above, but without the opening curly bracket. That's needed for nesting. + pub fn to_css_block_without_opening( + &self, + guard: &SharedRwLockReadGuard, + dest: &mut CssStringWriter, + ) -> fmt::Result { for rule in self.0.iter() { dest.write_str("\n ")?; rule.to_css(guard, dest)?; diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 0a7ce5dac17..fd8b80c2c45 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -788,6 +788,7 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> { Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule { selectors, block, + rules: None, // TODO(nesting) source_location: start.source_location(), })))) } diff --git a/components/style/stylesheets/style_rule.rs b/components/style/stylesheets/style_rule.rs index 1d4a2e27316..f2dc78b69f8 100644 --- a/components/style/stylesheets/style_rule.rs +++ b/components/style/stylesheets/style_rule.rs @@ -6,8 +6,8 @@ use crate::properties::PropertyDeclarationBlock; use crate::selector_parser::SelectorImpl; -use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; -use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; +use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; +use crate::stylesheets::CssRules; use crate::str::CssStringWriter; use cssparser::SourceLocation; #[cfg(feature = "gecko")] @@ -25,6 +25,8 @@ pub struct StyleRule { pub selectors: SelectorList, /// The declaration block with the properties it contains. pub block: Arc>, + /// The nested rules to this style rule. Only non-`None` when nesting is enabled. + pub rules: Option>>, /// The location in the sheet where it was found. pub source_location: SourceLocation, } @@ -35,11 +37,15 @@ impl DeepCloneWithLock for StyleRule { &self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard, - _params: &DeepCloneParams, + params: &DeepCloneParams, ) -> StyleRule { StyleRule { selectors: self.selectors.clone(), block: Arc::new(lock.wrap(self.block.read_with(guard).clone())), + rules: self.rules.as_ref().map(|rules| { + let rules = rules.read_with(guard); + Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))) + }), source_location: self.source_location.clone(), } } @@ -53,6 +59,9 @@ impl StyleRule { n += self.selectors.0.size_of(ops); n += self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops); + if let Some(ref rules) = self.rules { + n += rules.unconditional_shallow_size_of(ops) + rules.read_with(guard).size_of(guard, ops) + } n } } @@ -61,19 +70,32 @@ impl ToCssWithGuard for StyleRule { /// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { use cssparser::ToCss; - // Step 1 self.selectors.to_css(dest)?; + dest.write_str(" {")?; + // Step 2 - dest.write_str(" { ")?; - // Step 3 let declaration_block = self.block.read_with(guard); - declaration_block.to_css(dest)?; - // Step 4 - if !declaration_block.declarations().is_empty() { - dest.write_char(' ')?; + let has_declarations = !declaration_block.declarations().is_empty(); + + // Step 3 + if let Some(ref rules) = self.rules { + let rules = rules.read_with(guard); + // Step 6 (here because it's more convenient) + if !rules.is_empty() { + if has_declarations { + dest.write_str("\n ")?; + declaration_block.to_css(dest)?; + } + return rules.to_css_block_without_opening(guard, dest) + } } - // Step 5 - dest.write_char('}') + + // Steps 4 & 5 + if has_declarations { + dest.write_char(' ')?; + declaration_block.to_css(dest)?; + } + dest.write_str(" }") } }