/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! Style sheets and their CSS rules. pub mod container_rule; mod counter_style_rule; mod document_rule; mod font_face_rule; pub mod font_feature_values_rule; pub mod font_palette_values_rule; pub mod import_rule; pub mod keyframes_rule; pub mod layer_rule; mod loader; mod margin_rule; mod media_rule; mod namespace_rule; mod nested_declarations_rule; pub mod origin; mod page_rule; pub mod position_try_rule; mod property_rule; mod rule_list; mod rule_parser; mod rules_iterator; pub mod scope_rule; mod starting_style_rule; mod style_rule; mod stylesheet; pub mod supports_rule; #[cfg(feature = "gecko")] use crate::gecko_bindings::sugar::refptr::RefCounted; #[cfg(feature = "gecko")] use crate::gecko_bindings::{bindings, structs}; use crate::parser::{NestingContext, ParserContext}; use crate::properties::{parse_property_declaration_list, PropertyDeclarationBlock}; use crate::shared_lock::{DeepCloneWithLock, Locked}; use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; use crate::str::CssStringWriter; use cssparser::{parse_one_rule, Parser, ParserInput}; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use servo_arc::Arc; use std::borrow::Cow; use std::fmt::{self, Write}; #[cfg(feature = "gecko")] use std::mem::{self, ManuallyDrop}; use style_traits::ParsingMode; use to_shmem::{SharedMemoryBuilder, ToShmem}; pub use self::container_rule::ContainerRule; pub use self::counter_style_rule::CounterStyleRule; pub use self::document_rule::DocumentRule; pub use self::font_face_rule::FontFaceRule; pub use self::font_feature_values_rule::FontFeatureValuesRule; pub use self::font_palette_values_rule::FontPaletteValuesRule; pub use self::import_rule::ImportRule; pub use self::keyframes_rule::KeyframesRule; pub use self::layer_rule::{LayerBlockRule, LayerStatementRule}; pub use self::loader::StylesheetLoader; pub use self::margin_rule::{MarginRule, MarginRuleType}; pub use self::media_rule::MediaRule; pub use self::namespace_rule::NamespaceRule; pub use self::nested_declarations_rule::NestedDeclarationsRule; pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter}; pub use self::page_rule::{PagePseudoClassFlags, PageRule, PageSelector, PageSelectors}; pub use self::position_try_rule::PositionTryRule; pub use self::property_rule::PropertyRule; pub use self::rule_list::{CssRules, CssRulesHelpers}; pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser}; pub use self::rules_iterator::{AllRules, EffectiveRules}; pub use self::rules_iterator::{ EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator, }; pub use self::scope_rule::ScopeRule; pub use self::starting_style_rule::StartingStyleRule; pub use self::style_rule::StyleRule; pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind}; pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet}; pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets}; pub use self::supports_rule::SupportsRule; /// The CORS mode used for a CSS load. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] pub enum CorsMode { /// No CORS mode, so cross-origin loads can be done. None, /// Anonymous CORS request. Anonymous, } /// Extra data that the backend may need to resolve url values. /// /// If the usize's lowest bit is 0, then this is a strong reference to a /// structs::URLExtraData object. /// /// Otherwise, shifting the usize's bits the right by one gives the /// UserAgentStyleSheetID value corresponding to the style sheet whose /// URLExtraData this is, which is stored in URLExtraData_sShared. We don't /// hold a strong reference to that object from here, but we rely on that /// array's objects being held alive until shutdown. /// /// We use this packed representation rather than an enum so that /// `from_ptr_ref` can work. #[cfg(feature = "gecko")] // Although deriving MallocSizeOf means it always returns 0, that is fine because UrlExtraData // objects are reference-counted. #[derive(MallocSizeOf, PartialEq)] #[repr(C)] pub struct UrlExtraData(usize); /// Extra data that the backend may need to resolve url values. #[cfg(feature = "servo")] #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] pub struct UrlExtraData(#[ignore_malloc_size_of = "Arc"] pub Arc<::url::Url>); #[cfg(feature = "servo")] impl UrlExtraData { /// True if this URL scheme is chrome. pub fn chrome_rules_enabled(&self) -> bool { self.0.scheme() == "chrome" } /// Get the interior Url as a string. pub fn as_str(&self) -> &str { self.0.as_str() } } #[cfg(feature = "servo")] impl From<::url::Url> for UrlExtraData { fn from(url: ::url::Url) -> Self { Self(Arc::new(url)) } } #[cfg(not(feature = "gecko"))] impl ToShmem for UrlExtraData { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result { unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented"); } } #[cfg(feature = "gecko")] impl Clone for UrlExtraData { fn clone(&self) -> UrlExtraData { UrlExtraData::new(self.ptr()) } } #[cfg(feature = "gecko")] impl Drop for UrlExtraData { fn drop(&mut self) { // No need to release when we have an index into URLExtraData_sShared. if self.0 & 1 == 0 { unsafe { self.as_ref().release(); } } } } #[cfg(feature = "gecko")] impl ToShmem for UrlExtraData { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result { if self.0 & 1 == 0 { let shared_extra_datas = unsafe { std::ptr::addr_of!(structs::URLExtraData_sShared) .as_ref() .unwrap() }; let self_ptr = self.as_ref() as *const _ as *mut _; let sheet_id = shared_extra_datas .iter() .position(|r| r.mRawPtr == self_ptr); let sheet_id = match sheet_id { Some(id) => id, None => { return Err(String::from( "ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \ URLExtraData::sShared", )); }, }; Ok(ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1))) } else { Ok(ManuallyDrop::new(UrlExtraData(self.0))) } } } #[cfg(feature = "gecko")] impl UrlExtraData { /// Create a new UrlExtraData wrapping a pointer to the specified Gecko /// URLExtraData object. pub fn new(ptr: *mut structs::URLExtraData) -> UrlExtraData { unsafe { (*ptr).addref(); } UrlExtraData(ptr as usize) } /// True if this URL scheme is chrome. #[inline] pub fn chrome_rules_enabled(&self) -> bool { self.as_ref().mChromeRulesEnabled } /// Create a reference to this `UrlExtraData` from a reference to pointer. /// /// The pointer must be valid and non null. /// /// This method doesn't touch refcount. #[inline] pub unsafe fn from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self { mem::transmute(ptr) } /// Returns a pointer to the Gecko URLExtraData object. pub fn ptr(&self) -> *mut structs::URLExtraData { if self.0 & 1 == 0 { self.0 as *mut structs::URLExtraData } else { unsafe { let sheet_id = self.0 >> 1; structs::URLExtraData_sShared[sheet_id].mRawPtr } } } fn as_ref(&self) -> &structs::URLExtraData { unsafe { &*(self.ptr() as *const structs::URLExtraData) } } } #[cfg(feature = "gecko")] impl fmt::Debug for UrlExtraData { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { macro_rules! define_debug_struct { ($struct_name:ident, $gecko_class:ident, $debug_fn:ident) => { struct $struct_name(*mut structs::$gecko_class); impl fmt::Debug for $struct_name { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { use nsstring::nsCString; let mut spec = nsCString::new(); unsafe { bindings::$debug_fn(self.0, &mut spec); } spec.fmt(formatter) } } }; } define_debug_struct!(DebugURI, nsIURI, Gecko_nsIURI_Debug); define_debug_struct!( DebugReferrerInfo, nsIReferrerInfo, Gecko_nsIReferrerInfo_Debug ); formatter .debug_struct("URLExtraData") .field("chrome_rules_enabled", &self.chrome_rules_enabled()) .field("base", &DebugURI(self.as_ref().mBaseURI.raw())) .field( "referrer", &DebugReferrerInfo(self.as_ref().mReferrerInfo.raw()), ) .finish() } } // XXX We probably need to figure out whether we should mark Eq here. // It is currently marked so because properties::UnparsedValue wants Eq. #[cfg(feature = "gecko")] impl Eq for UrlExtraData {} /// Serialize a page or style rule, starting with the opening brace. /// /// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule /// /// This is not properly specified for page-rules, but we will apply the /// same process. fn style_or_page_rule_to_css( rules: Option<&Arc>>, block: &Locked, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter, ) -> fmt::Result { // Write the opening brace. The caller needs to serialize up to this point. dest.write_char('{')?; // Step 2 let declaration_block = block.read_with(guard); let has_declarations = !declaration_block.declarations().is_empty(); // Step 3 if let Some(ref rules) = 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); } } // Steps 4 & 5 if has_declarations { dest.write_char(' ')?; declaration_block.to_css(dest)?; } dest.write_str(" }") } /// A CSS rule. /// /// TODO(emilio): Lots of spec links should be around. #[derive(Clone, Debug, ToShmem)] #[allow(missing_docs)] pub enum CssRule { Style(Arc>), // No Charset here, CSSCharsetRule has been removed from CSSOM // https://drafts.csswg.org/cssom/#changes-from-5-december-2013 Namespace(Arc), Import(Arc>), Media(Arc), Container(Arc), FontFace(Arc>), FontFeatureValues(Arc), FontPaletteValues(Arc), CounterStyle(Arc>), Keyframes(Arc>), Margin(Arc), Supports(Arc), Page(Arc>), Property(Arc), Document(Arc), LayerBlock(Arc), LayerStatement(Arc), Scope(Arc), StartingStyle(Arc), PositionTry(Arc>), NestedDeclarations(Arc>), } impl CssRule { /// Measure heap usage. #[cfg(feature = "gecko")] fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { match *self { // Not all fields are currently fully measured. Extra measurement // may be added later. CssRule::Namespace(_) => 0, // We don't need to measure ImportRule::stylesheet because we measure // it on the C++ side in the child list of the ServoStyleSheet. CssRule::Import(_) => 0, CssRule::Style(ref lock) => { lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) }, CssRule::Media(ref arc) => { arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) }, CssRule::Container(ref arc) => { arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) }, CssRule::FontFace(_) => 0, CssRule::FontFeatureValues(_) => 0, CssRule::FontPaletteValues(_) => 0, CssRule::CounterStyle(_) => 0, CssRule::Keyframes(_) => 0, CssRule::Margin(ref arc) => { arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) }, CssRule::Supports(ref arc) => { arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) }, CssRule::Page(ref lock) => { lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) }, CssRule::Property(ref rule) => { rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops) }, CssRule::Document(ref arc) => { arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) }, CssRule::StartingStyle(ref arc) => { arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) }, // TODO(emilio): Add memory reporting for these rules. CssRule::LayerBlock(_) | CssRule::LayerStatement(_) => 0, CssRule::Scope(ref rule) => { rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops) }, CssRule::PositionTry(ref lock) => { lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) }, CssRule::NestedDeclarations(ref lock) => { lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) }, } } } /// https://drafts.csswg.org/cssom-1/#dom-cssrule-type #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)] #[repr(u8)] pub enum CssRuleType { // https://drafts.csswg.org/cssom/#the-cssrule-interface Style = 1, // Charset = 2, // Historical Import = 3, Media = 4, FontFace = 5, Page = 6, // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl Keyframes = 7, Keyframe = 8, // https://drafts.csswg.org/cssom/#the-cssrule-interface Margin = 9, Namespace = 10, // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface CounterStyle = 11, // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface Supports = 12, // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface Document = 13, // https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues FontFeatureValues = 14, // After viewport, all rules should return 0 from the API, but we still need // a constant somewhere. LayerBlock = 16, LayerStatement = 17, Container = 18, FontPaletteValues = 19, // 20 is an arbitrary number to use for Property. Property = 20, Scope = 21, // https://drafts.csswg.org/css-transitions-2/#the-cssstartingstylerule-interface StartingStyle = 22, // https://drafts.csswg.org/css-anchor-position-1/#om-position-try PositionTry = 23, // https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule NestedDeclarations = 24, } impl CssRuleType { /// Returns a bit that identifies this rule type. #[inline] pub const fn bit(self) -> u32 { 1 << self as u32 } } /// Set of rule types. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct CssRuleTypes(u32); impl From for CssRuleTypes { fn from(ty: CssRuleType) -> Self { Self(ty.bit()) } } impl CssRuleTypes { /// Rules where !important declarations are forbidden. pub const IMPORTANT_FORBIDDEN: Self = Self(CssRuleType::PositionTry.bit() | CssRuleType::Keyframe.bit()); /// Returns whether the rule is in the current set. #[inline] pub fn contains(self, ty: CssRuleType) -> bool { self.0 & ty.bit() != 0 } /// Returns all the rules specified in the set. #[inline] pub fn bits(self) -> u32 { self.0 } /// Creates a raw CssRuleTypes bitfield. #[inline] pub fn from_bits(bits: u32) -> Self { Self(bits) } /// Returns whether the rule set is empty. #[inline] pub fn is_empty(self) -> bool { self.0 == 0 } /// Inserts a rule type into the set. #[inline] pub fn insert(&mut self, ty: CssRuleType) { self.0 |= ty.bit() } /// Returns whether any of the types intersect. #[inline] pub fn intersects(self, other: Self) -> bool { self.0 & other.0 != 0 } } #[allow(missing_docs)] pub enum RulesMutateError { Syntax, IndexSize, HierarchyRequest, InvalidState, } impl CssRule { /// Returns the CSSOM rule type of this rule. pub fn rule_type(&self) -> CssRuleType { match *self { CssRule::Style(_) => CssRuleType::Style, CssRule::Import(_) => CssRuleType::Import, CssRule::Media(_) => CssRuleType::Media, CssRule::FontFace(_) => CssRuleType::FontFace, CssRule::FontFeatureValues(_) => CssRuleType::FontFeatureValues, CssRule::FontPaletteValues(_) => CssRuleType::FontPaletteValues, CssRule::CounterStyle(_) => CssRuleType::CounterStyle, CssRule::Keyframes(_) => CssRuleType::Keyframes, CssRule::Margin(_) => CssRuleType::Margin, CssRule::Namespace(_) => CssRuleType::Namespace, CssRule::Supports(_) => CssRuleType::Supports, CssRule::Page(_) => CssRuleType::Page, CssRule::Property(_) => CssRuleType::Property, CssRule::Document(_) => CssRuleType::Document, CssRule::LayerBlock(_) => CssRuleType::LayerBlock, CssRule::LayerStatement(_) => CssRuleType::LayerStatement, CssRule::Container(_) => CssRuleType::Container, CssRule::Scope(_) => CssRuleType::Scope, CssRule::StartingStyle(_) => CssRuleType::StartingStyle, CssRule::PositionTry(_) => CssRuleType::PositionTry, CssRule::NestedDeclarations(_) => CssRuleType::NestedDeclarations, } } /// Parse a CSS rule. /// /// This mostly implements steps 3..7 of https://drafts.csswg.org/cssom/#insert-a-css-rule pub fn parse( css: &str, insert_rule_context: InsertRuleContext, parent_stylesheet_contents: &StylesheetContents, shared_lock: &SharedRwLock, loader: Option<&dyn StylesheetLoader>, allow_import_rules: AllowImportRules, ) -> Result { let url_data = parent_stylesheet_contents.url_data.read(); let namespaces = parent_stylesheet_contents.namespaces.read(); let mut context = ParserContext::new( parent_stylesheet_contents.origin, &url_data, None, ParsingMode::DEFAULT, parent_stylesheet_contents.quirks_mode, Cow::Borrowed(&*namespaces), None, None, ); // Override the nesting context with existing data. context.nesting_context = NestingContext::new( insert_rule_context.containing_rule_types, insert_rule_context.parse_relative_rule_type, ); let state = if !insert_rule_context.containing_rule_types.is_empty() { State::Body } else if insert_rule_context.index == 0 { State::Start } else { let index = insert_rule_context.index; insert_rule_context.max_rule_state_at_index(index - 1) }; let mut input = ParserInput::new(css); let mut input = Parser::new(&mut input); // nested rules are in the body state let mut parser = TopLevelRuleParser { context, shared_lock: &shared_lock, loader, state, dom_error: None, insert_rule_context: Some(insert_rule_context), allow_import_rules, declaration_parser_state: Default::default(), first_declaration_block: Default::default(), wants_first_declaration_block: false, error_reporting_state: Default::default(), rules: Default::default(), }; if input.try_parse(|input| parse_one_rule(input, &mut parser)).is_ok() { return Ok(parser.rules.pop().unwrap()); } let error = parser.dom_error.take().unwrap_or(RulesMutateError::Syntax); // If new rule is a syntax error, and nested is set, perform the following substeps: if matches!(error, RulesMutateError::Syntax) && parser.can_parse_declarations() { let declarations = parse_property_declaration_list(&parser.context, &mut input, &[]); if !declarations.is_empty() { return Ok(CssRule::NestedDeclarations(Arc::new(parser.shared_lock.wrap(NestedDeclarationsRule { block: Arc::new(parser.shared_lock.wrap(declarations)), source_location: input.current_source_location(), })))); } } Err(error) } } impl DeepCloneWithLock for CssRule { /// Deep clones this CssRule. fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> CssRule { match *self { CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()), CssRule::Import(ref arc) => { let rule = arc.read_with(guard).deep_clone_with_lock(lock, guard); CssRule::Import(Arc::new(lock.wrap(rule))) }, CssRule::Style(ref arc) => { let rule = arc.read_with(guard); CssRule::Style(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard)))) }, CssRule::Container(ref arc) => { CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard))) }, CssRule::Media(ref arc) => { CssRule::Media(Arc::new(arc.deep_clone_with_lock(lock, guard))) }, CssRule::FontFace(ref arc) => { let rule = arc.read_with(guard); CssRule::FontFace(Arc::new(lock.wrap(rule.clone()))) }, CssRule::FontFeatureValues(ref arc) => CssRule::FontFeatureValues(arc.clone()), CssRule::FontPaletteValues(ref arc) => CssRule::FontPaletteValues(arc.clone()), CssRule::CounterStyle(ref arc) => { let rule = arc.read_with(guard); CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone()))) }, CssRule::Keyframes(ref arc) => { let rule = arc.read_with(guard); CssRule::Keyframes(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard)))) }, CssRule::Margin(ref arc) => { CssRule::Margin(Arc::new(arc.deep_clone_with_lock(lock, guard))) }, CssRule::Supports(ref arc) => { CssRule::Supports(Arc::new(arc.deep_clone_with_lock(lock, guard))) }, CssRule::Page(ref arc) => { let rule = arc.read_with(guard); CssRule::Page(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard)))) }, CssRule::Property(ref arc) => { // @property rules are immutable, so we don't need any of the `Locked` // shenanigans, actually, and can just share the rule. CssRule::Property(arc.clone()) }, CssRule::Document(ref arc) => { CssRule::Document(Arc::new(arc.deep_clone_with_lock(lock, guard))) }, CssRule::LayerStatement(ref arc) => CssRule::LayerStatement(arc.clone()), CssRule::LayerBlock(ref arc) => { CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard))) }, CssRule::Scope(ref arc) => { CssRule::Scope(Arc::new(arc.deep_clone_with_lock(lock, guard))) }, CssRule::StartingStyle(ref arc) => { CssRule::StartingStyle(Arc::new(arc.deep_clone_with_lock(lock, guard))) }, CssRule::PositionTry(ref arc) => { let rule = arc.read_with(guard); CssRule::PositionTry(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard)))) }, CssRule::NestedDeclarations(ref arc) => { let decls = arc.read_with(guard); CssRule::NestedDeclarations(Arc::new(lock.wrap(decls.clone()))) }, } } } impl ToCssWithGuard for CssRule { // https://drafts.csswg.org/cssom/#serialize-a-css-rule fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { match *self { CssRule::Namespace(ref rule) => rule.to_css(guard, dest), CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::FontFeatureValues(ref rule) => rule.to_css(guard, dest), CssRule::FontPaletteValues(ref rule) => rule.to_css(guard, dest), CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Margin(ref rule) => rule.to_css(guard, dest), CssRule::Media(ref rule) => rule.to_css(guard, dest), CssRule::Supports(ref rule) => rule.to_css(guard, dest), CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Property(ref rule) => rule.to_css(guard, dest), CssRule::Document(ref rule) => rule.to_css(guard, dest), CssRule::LayerBlock(ref rule) => rule.to_css(guard, dest), CssRule::LayerStatement(ref rule) => rule.to_css(guard, dest), CssRule::Container(ref rule) => rule.to_css(guard, dest), CssRule::Scope(ref rule) => rule.to_css(guard, dest), CssRule::StartingStyle(ref rule) => rule.to_css(guard, dest), CssRule::PositionTry(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::NestedDeclarations(ref lock) => lock.read_with(guard).to_css(guard, dest), } } }