style/stylesheets/
mod.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Style sheets and their CSS rules.
6
7pub mod container_rule;
8mod counter_style_rule;
9mod document_rule;
10mod font_face_rule;
11pub mod font_feature_values_rule;
12pub mod font_palette_values_rule;
13pub mod import_rule;
14pub mod keyframes_rule;
15pub mod layer_rule;
16mod loader;
17mod margin_rule;
18mod media_rule;
19mod namespace_rule;
20mod nested_declarations_rule;
21pub mod origin;
22mod page_rule;
23pub mod position_try_rule;
24mod property_rule;
25mod rule_list;
26mod rule_parser;
27mod rules_iterator;
28pub mod scope_rule;
29mod starting_style_rule;
30mod style_rule;
31mod stylesheet;
32pub mod supports_rule;
33
34#[cfg(feature = "gecko")]
35use crate::gecko_bindings::sugar::refptr::RefCounted;
36#[cfg(feature = "gecko")]
37use crate::gecko_bindings::{bindings, structs};
38use crate::parser::{NestingContext, ParserContext};
39use crate::properties::{parse_property_declaration_list, PropertyDeclarationBlock};
40use crate::shared_lock::{DeepCloneWithLock, Locked};
41use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
42use cssparser::{parse_one_rule, Parser, ParserInput};
43#[cfg(feature = "gecko")]
44use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
45use servo_arc::Arc;
46use std::borrow::Cow;
47use std::fmt::{self, Write};
48#[cfg(feature = "gecko")]
49use std::mem::{self, ManuallyDrop};
50use style_traits::{CssStringWriter, ParsingMode};
51use to_shmem::{SharedMemoryBuilder, ToShmem};
52
53pub use self::container_rule::ContainerRule;
54pub use self::counter_style_rule::CounterStyleRule;
55pub use self::document_rule::DocumentRule;
56pub use self::font_face_rule::FontFaceRule;
57pub use self::font_feature_values_rule::FontFeatureValuesRule;
58pub use self::font_palette_values_rule::FontPaletteValuesRule;
59pub use self::import_rule::ImportRule;
60pub use self::keyframes_rule::KeyframesRule;
61pub use self::layer_rule::{LayerBlockRule, LayerStatementRule};
62pub use self::loader::StylesheetLoader;
63pub use self::margin_rule::{MarginRule, MarginRuleType};
64pub use self::media_rule::MediaRule;
65pub use self::namespace_rule::NamespaceRule;
66pub use self::nested_declarations_rule::NestedDeclarationsRule;
67pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
68pub use self::page_rule::{PagePseudoClassFlags, PageRule, PageSelector, PageSelectors};
69pub use self::position_try_rule::PositionTryRule;
70pub use self::property_rule::PropertyRule;
71pub use self::rule_list::{CssRules, CssRulesHelpers};
72pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser};
73pub use self::rules_iterator::{AllRules, EffectiveRules};
74pub use self::rules_iterator::{
75    EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator,
76};
77pub use self::scope_rule::ScopeRule;
78pub use self::starting_style_rule::StartingStyleRule;
79pub use self::style_rule::StyleRule;
80pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind};
81pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};
82pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
83pub use self::supports_rule::SupportsRule;
84
85/// The CORS mode used for a CSS load.
86#[repr(u8)]
87#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
88pub enum CorsMode {
89    /// No CORS mode, so cross-origin loads can be done.
90    None,
91    /// Anonymous CORS request.
92    Anonymous,
93}
94
95/// Extra data that the backend may need to resolve url values.
96///
97/// If the usize's lowest bit is 0, then this is a strong reference to a
98/// structs::URLExtraData object.
99///
100/// Otherwise, shifting the usize's bits the right by one gives the
101/// UserAgentStyleSheetID value corresponding to the style sheet whose
102/// URLExtraData this is, which is stored in URLExtraData_sShared.  We don't
103/// hold a strong reference to that object from here, but we rely on that
104/// array's objects being held alive until shutdown.
105///
106/// We use this packed representation rather than an enum so that
107/// `from_ptr_ref` can work.
108#[cfg(feature = "gecko")]
109// Although deriving MallocSizeOf means it always returns 0, that is fine because UrlExtraData
110// objects are reference-counted.
111#[derive(MallocSizeOf, PartialEq)]
112#[repr(C)]
113pub struct UrlExtraData(usize);
114
115/// Extra data that the backend may need to resolve url values.
116#[cfg(feature = "servo")]
117#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
118pub struct UrlExtraData(#[ignore_malloc_size_of = "Arc"] pub Arc<::url::Url>);
119
120#[cfg(feature = "servo")]
121impl UrlExtraData {
122    /// True if this URL scheme is chrome.
123    pub fn chrome_rules_enabled(&self) -> bool {
124        self.0.scheme() == "chrome"
125    }
126
127    /// Get the interior Url as a string.
128    pub fn as_str(&self) -> &str {
129        self.0.as_str()
130    }
131}
132
133#[cfg(feature = "servo")]
134impl From<::url::Url> for UrlExtraData {
135    fn from(url: ::url::Url) -> Self {
136        Self(Arc::new(url))
137    }
138}
139
140#[cfg(not(feature = "gecko"))]
141impl ToShmem for UrlExtraData {
142    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
143        unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented");
144    }
145}
146
147#[cfg(feature = "gecko")]
148impl Clone for UrlExtraData {
149    fn clone(&self) -> UrlExtraData {
150        UrlExtraData::new(self.ptr())
151    }
152}
153
154#[cfg(feature = "gecko")]
155impl Drop for UrlExtraData {
156    fn drop(&mut self) {
157        // No need to release when we have an index into URLExtraData_sShared.
158        if self.0 & 1 == 0 {
159            unsafe {
160                self.as_ref().release();
161            }
162        }
163    }
164}
165
166#[cfg(feature = "gecko")]
167impl ToShmem for UrlExtraData {
168    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
169        if self.0 & 1 == 0 {
170            let shared_extra_datas = unsafe {
171                std::ptr::addr_of!(structs::URLExtraData_sShared)
172                    .as_ref()
173                    .unwrap()
174            };
175            let self_ptr = self.as_ref() as *const _ as *mut _;
176            let sheet_id = shared_extra_datas
177                .iter()
178                .position(|r| r.mRawPtr == self_ptr);
179            let sheet_id = match sheet_id {
180                Some(id) => id,
181                None => {
182                    return Err(String::from(
183                        "ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \
184                         URLExtraData::sShared",
185                    ));
186                },
187            };
188            Ok(ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1)))
189        } else {
190            Ok(ManuallyDrop::new(UrlExtraData(self.0)))
191        }
192    }
193}
194
195#[cfg(feature = "gecko")]
196impl UrlExtraData {
197    /// Create a new UrlExtraData wrapping a pointer to the specified Gecko
198    /// URLExtraData object.
199    pub fn new(ptr: *mut structs::URLExtraData) -> UrlExtraData {
200        unsafe {
201            (*ptr).addref();
202        }
203        UrlExtraData(ptr as usize)
204    }
205
206    /// True if this URL scheme is chrome.
207    #[inline]
208    pub fn chrome_rules_enabled(&self) -> bool {
209        self.as_ref().mChromeRulesEnabled
210    }
211
212    /// Create a reference to this `UrlExtraData` from a reference to pointer.
213    ///
214    /// The pointer must be valid and non null.
215    ///
216    /// This method doesn't touch refcount.
217    #[inline]
218    pub unsafe fn from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self {
219        mem::transmute(ptr)
220    }
221
222    /// Returns a pointer to the Gecko URLExtraData object.
223    pub fn ptr(&self) -> *mut structs::URLExtraData {
224        if self.0 & 1 == 0 {
225            self.0 as *mut structs::URLExtraData
226        } else {
227            unsafe {
228                let sheet_id = self.0 >> 1;
229                structs::URLExtraData_sShared[sheet_id].mRawPtr
230            }
231        }
232    }
233
234    fn as_ref(&self) -> &structs::URLExtraData {
235        unsafe { &*(self.ptr() as *const structs::URLExtraData) }
236    }
237}
238
239#[cfg(feature = "gecko")]
240impl fmt::Debug for UrlExtraData {
241    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
242        macro_rules! define_debug_struct {
243            ($struct_name:ident, $gecko_class:ident, $debug_fn:ident) => {
244                struct $struct_name(*mut structs::$gecko_class);
245                impl fmt::Debug for $struct_name {
246                    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
247                        use nsstring::nsCString;
248                        let mut spec = nsCString::new();
249                        unsafe {
250                            bindings::$debug_fn(self.0, &mut spec);
251                        }
252                        spec.fmt(formatter)
253                    }
254                }
255            };
256        }
257
258        define_debug_struct!(DebugURI, nsIURI, Gecko_nsIURI_Debug);
259        define_debug_struct!(
260            DebugReferrerInfo,
261            nsIReferrerInfo,
262            Gecko_nsIReferrerInfo_Debug
263        );
264
265        formatter
266            .debug_struct("URLExtraData")
267            .field("chrome_rules_enabled", &self.chrome_rules_enabled())
268            .field("base", &DebugURI(self.as_ref().mBaseURI.raw()))
269            .field(
270                "referrer",
271                &DebugReferrerInfo(self.as_ref().mReferrerInfo.raw()),
272            )
273            .finish()
274    }
275}
276
277// XXX We probably need to figure out whether we should mark Eq here.
278// It is currently marked so because properties::UnparsedValue wants Eq.
279#[cfg(feature = "gecko")]
280impl Eq for UrlExtraData {}
281
282/// Serialize a page or style rule, starting with the opening brace.
283///
284/// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule
285///
286/// This is not properly specified for page-rules, but we will apply the
287/// same process.
288fn style_or_page_rule_to_css(
289    rules: Option<&Arc<Locked<CssRules>>>,
290    block: &Locked<PropertyDeclarationBlock>,
291    guard: &SharedRwLockReadGuard,
292    dest: &mut CssStringWriter,
293) -> fmt::Result {
294    // Write the opening brace. The caller needs to serialize up to this point.
295    dest.write_char('{')?;
296
297    // Step 2
298    let declaration_block = block.read_with(guard);
299    let has_declarations = !declaration_block.declarations().is_empty();
300
301    // Step 3
302    if let Some(ref rules) = rules {
303        let rules = rules.read_with(guard);
304        // Step 6 (here because it's more convenient)
305        if !rules.is_empty() {
306            if has_declarations {
307                dest.write_str("\n  ")?;
308                declaration_block.to_css(dest)?;
309            }
310            return rules.to_css_block_without_opening(guard, dest);
311        }
312    }
313
314    // Steps 4 & 5
315    if has_declarations {
316        dest.write_char(' ')?;
317        declaration_block.to_css(dest)?;
318    }
319    dest.write_str(" }")
320}
321
322/// A CSS rule.
323///
324/// TODO(emilio): Lots of spec links should be around.
325#[derive(Clone, Debug, ToShmem)]
326#[allow(missing_docs)]
327pub enum CssRule {
328    Style(Arc<Locked<StyleRule>>),
329    // No Charset here, CSSCharsetRule has been removed from CSSOM
330    // https://drafts.csswg.org/cssom/#changes-from-5-december-2013
331    Namespace(Arc<NamespaceRule>),
332    Import(Arc<Locked<ImportRule>>),
333    Media(Arc<MediaRule>),
334    Container(Arc<ContainerRule>),
335    FontFace(Arc<Locked<FontFaceRule>>),
336    FontFeatureValues(Arc<FontFeatureValuesRule>),
337    FontPaletteValues(Arc<FontPaletteValuesRule>),
338    CounterStyle(Arc<Locked<CounterStyleRule>>),
339    Keyframes(Arc<Locked<KeyframesRule>>),
340    Margin(Arc<MarginRule>),
341    Supports(Arc<SupportsRule>),
342    Page(Arc<Locked<PageRule>>),
343    Property(Arc<PropertyRule>),
344    Document(Arc<DocumentRule>),
345    LayerBlock(Arc<LayerBlockRule>),
346    LayerStatement(Arc<LayerStatementRule>),
347    Scope(Arc<ScopeRule>),
348    StartingStyle(Arc<StartingStyleRule>),
349    PositionTry(Arc<Locked<PositionTryRule>>),
350    NestedDeclarations(Arc<Locked<NestedDeclarationsRule>>),
351}
352
353impl CssRule {
354    /// Measure heap usage.
355    #[cfg(feature = "gecko")]
356    fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
357        match *self {
358            // Not all fields are currently fully measured. Extra measurement
359            // may be added later.
360            CssRule::Namespace(_) => 0,
361
362            // We don't need to measure ImportRule::stylesheet because we measure
363            // it on the C++ side in the child list of the ServoStyleSheet.
364            CssRule::Import(_) => 0,
365
366            CssRule::Style(ref lock) => {
367                lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
368            },
369            CssRule::Media(ref arc) => {
370                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
371            },
372            CssRule::Container(ref arc) => {
373                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
374            },
375            CssRule::FontFace(_) => 0,
376            CssRule::FontFeatureValues(_) => 0,
377            CssRule::FontPaletteValues(_) => 0,
378            CssRule::CounterStyle(_) => 0,
379            CssRule::Keyframes(_) => 0,
380            CssRule::Margin(ref arc) => {
381                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
382            },
383            CssRule::Supports(ref arc) => {
384                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
385            },
386            CssRule::Page(ref lock) => {
387                lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
388            },
389            CssRule::Property(ref rule) => {
390                rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops)
391            },
392            CssRule::Document(ref arc) => {
393                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
394            },
395            CssRule::StartingStyle(ref arc) => {
396                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
397            },
398            // TODO(emilio): Add memory reporting for these rules.
399            CssRule::LayerBlock(_) | CssRule::LayerStatement(_) => 0,
400            CssRule::Scope(ref rule) => {
401                rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops)
402            },
403            CssRule::PositionTry(ref lock) => {
404                lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
405            },
406            CssRule::NestedDeclarations(ref lock) => {
407                lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
408            },
409        }
410    }
411
412    fn is_empty_nested_declarations(&self, guard: &SharedRwLockReadGuard) -> bool {
413        match *self {
414            CssRule::NestedDeclarations(ref lock) => {
415                lock.read_with(guard).block.read_with(guard).is_empty()
416            },
417            _ => false,
418        }
419    }
420}
421
422/// https://drafts.csswg.org/cssom-1/#dom-cssrule-type
423#[allow(missing_docs)]
424#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
425#[repr(u8)]
426pub enum CssRuleType {
427    // https://drafts.csswg.org/cssom/#the-cssrule-interface
428    Style = 1,
429    // Charset = 2, // Historical
430    Import = 3,
431    Media = 4,
432    FontFace = 5,
433    Page = 6,
434    // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl
435    Keyframes = 7,
436    Keyframe = 8,
437    // https://drafts.csswg.org/cssom/#the-cssrule-interface
438    Margin = 9,
439    Namespace = 10,
440    // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface
441    CounterStyle = 11,
442    // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface
443    Supports = 12,
444    // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface
445    Document = 13,
446    // https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues
447    FontFeatureValues = 14,
448    // After viewport, all rules should return 0 from the API, but we still need
449    // a constant somewhere.
450    LayerBlock = 16,
451    LayerStatement = 17,
452    Container = 18,
453    FontPaletteValues = 19,
454    // 20 is an arbitrary number to use for Property.
455    Property = 20,
456    Scope = 21,
457    // https://drafts.csswg.org/css-transitions-2/#the-cssstartingstylerule-interface
458    StartingStyle = 22,
459    // https://drafts.csswg.org/css-anchor-position-1/#om-position-try
460    PositionTry = 23,
461    // https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule
462    NestedDeclarations = 24,
463}
464
465impl CssRuleType {
466    /// Returns a bit that identifies this rule type.
467    #[inline]
468    pub const fn bit(self) -> u32 {
469        1 << self as u32
470    }
471}
472
473/// Set of rule types.
474#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
475pub struct CssRuleTypes(u32);
476
477impl From<CssRuleType> for CssRuleTypes {
478    fn from(ty: CssRuleType) -> Self {
479        Self(ty.bit())
480    }
481}
482
483impl CssRuleTypes {
484    /// Rules where !important declarations are forbidden.
485    pub const IMPORTANT_FORBIDDEN: Self =
486        Self(CssRuleType::PositionTry.bit() | CssRuleType::Keyframe.bit());
487
488    /// Returns whether the rule is in the current set.
489    #[inline]
490    pub fn contains(self, ty: CssRuleType) -> bool {
491        self.0 & ty.bit() != 0
492    }
493
494    /// Returns all the rules specified in the set.
495    #[inline]
496    pub fn bits(self) -> u32 {
497        self.0
498    }
499
500    /// Creates a raw CssRuleTypes bitfield.
501    #[inline]
502    pub fn from_bits(bits: u32) -> Self {
503        Self(bits)
504    }
505
506    /// Returns whether the rule set is empty.
507    #[inline]
508    pub fn is_empty(self) -> bool {
509        self.0 == 0
510    }
511
512    /// Inserts a rule type into the set.
513    #[inline]
514    pub fn insert(&mut self, ty: CssRuleType) {
515        self.0 |= ty.bit()
516    }
517
518    /// Returns whether any of the types intersect.
519    #[inline]
520    pub fn intersects(self, other: Self) -> bool {
521        self.0 & other.0 != 0
522    }
523}
524
525#[allow(missing_docs)]
526pub enum RulesMutateError {
527    Syntax,
528    IndexSize,
529    HierarchyRequest,
530    InvalidState,
531}
532
533impl CssRule {
534    /// Returns the CSSOM rule type of this rule.
535    pub fn rule_type(&self) -> CssRuleType {
536        match *self {
537            CssRule::Style(_) => CssRuleType::Style,
538            CssRule::Import(_) => CssRuleType::Import,
539            CssRule::Media(_) => CssRuleType::Media,
540            CssRule::FontFace(_) => CssRuleType::FontFace,
541            CssRule::FontFeatureValues(_) => CssRuleType::FontFeatureValues,
542            CssRule::FontPaletteValues(_) => CssRuleType::FontPaletteValues,
543            CssRule::CounterStyle(_) => CssRuleType::CounterStyle,
544            CssRule::Keyframes(_) => CssRuleType::Keyframes,
545            CssRule::Margin(_) => CssRuleType::Margin,
546            CssRule::Namespace(_) => CssRuleType::Namespace,
547            CssRule::Supports(_) => CssRuleType::Supports,
548            CssRule::Page(_) => CssRuleType::Page,
549            CssRule::Property(_) => CssRuleType::Property,
550            CssRule::Document(_) => CssRuleType::Document,
551            CssRule::LayerBlock(_) => CssRuleType::LayerBlock,
552            CssRule::LayerStatement(_) => CssRuleType::LayerStatement,
553            CssRule::Container(_) => CssRuleType::Container,
554            CssRule::Scope(_) => CssRuleType::Scope,
555            CssRule::StartingStyle(_) => CssRuleType::StartingStyle,
556            CssRule::PositionTry(_) => CssRuleType::PositionTry,
557            CssRule::NestedDeclarations(_) => CssRuleType::NestedDeclarations,
558        }
559    }
560
561    /// Parse a CSS rule.
562    ///
563    /// This mostly implements steps 3..7 of https://drafts.csswg.org/cssom/#insert-a-css-rule
564    pub fn parse(
565        css: &str,
566        insert_rule_context: InsertRuleContext,
567        parent_stylesheet_contents: &StylesheetContents,
568        shared_lock: &SharedRwLock,
569        loader: Option<&dyn StylesheetLoader>,
570        allow_import_rules: AllowImportRules,
571    ) -> Result<Self, RulesMutateError> {
572        let url_data = parent_stylesheet_contents.url_data.read();
573        let namespaces = parent_stylesheet_contents.namespaces.read();
574        let mut context = ParserContext::new(
575            parent_stylesheet_contents.origin,
576            &url_data,
577            None,
578            ParsingMode::DEFAULT,
579            parent_stylesheet_contents.quirks_mode,
580            Cow::Borrowed(&*namespaces),
581            None,
582            None,
583        );
584        // Override the nesting context with existing data.
585        context.nesting_context = NestingContext::new(
586            insert_rule_context.containing_rule_types,
587            insert_rule_context.parse_relative_rule_type,
588        );
589
590        let state = if !insert_rule_context.containing_rule_types.is_empty() {
591            State::Body
592        } else if insert_rule_context.index == 0 {
593            State::Start
594        } else {
595            let index = insert_rule_context.index;
596            insert_rule_context.max_rule_state_at_index(index - 1)
597        };
598
599        let mut input = ParserInput::new(css);
600        let mut input = Parser::new(&mut input);
601
602        // nested rules are in the body state
603        let mut parser = TopLevelRuleParser {
604            context,
605            shared_lock: &shared_lock,
606            loader,
607            state,
608            dom_error: None,
609            insert_rule_context: Some(insert_rule_context),
610            allow_import_rules,
611            declaration_parser_state: Default::default(),
612            first_declaration_block: Default::default(),
613            wants_first_declaration_block: false,
614            error_reporting_state: Default::default(),
615            rules: Default::default(),
616        };
617
618        if input
619            .try_parse(|input| parse_one_rule(input, &mut parser))
620            .is_ok()
621        {
622            return Ok(parser.rules.pop().unwrap());
623        }
624
625        let error = parser.dom_error.take().unwrap_or(RulesMutateError::Syntax);
626        // If new rule is a syntax error, and nested is set, perform the following substeps:
627        if matches!(error, RulesMutateError::Syntax) && parser.can_parse_declarations() {
628            let declarations = parse_property_declaration_list(&parser.context, &mut input, &[]);
629            if !declarations.is_empty() {
630                return Ok(CssRule::NestedDeclarations(Arc::new(
631                    parser.shared_lock.wrap(NestedDeclarationsRule {
632                        block: Arc::new(parser.shared_lock.wrap(declarations)),
633                        source_location: input.current_source_location(),
634                    }),
635                )));
636            }
637        }
638        Err(error)
639    }
640}
641
642impl DeepCloneWithLock for CssRule {
643    /// Deep clones this CssRule.
644    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> CssRule {
645        match *self {
646            CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()),
647            CssRule::Import(ref arc) => {
648                let rule = arc.read_with(guard).deep_clone_with_lock(lock, guard);
649                CssRule::Import(Arc::new(lock.wrap(rule)))
650            },
651            CssRule::Style(ref arc) => {
652                let rule = arc.read_with(guard);
653                CssRule::Style(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
654            },
655            CssRule::Container(ref arc) => {
656                CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard)))
657            },
658            CssRule::Media(ref arc) => {
659                CssRule::Media(Arc::new(arc.deep_clone_with_lock(lock, guard)))
660            },
661            CssRule::FontFace(ref arc) => {
662                let rule = arc.read_with(guard);
663                CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
664            },
665            CssRule::FontFeatureValues(ref arc) => CssRule::FontFeatureValues(arc.clone()),
666            CssRule::FontPaletteValues(ref arc) => CssRule::FontPaletteValues(arc.clone()),
667            CssRule::CounterStyle(ref arc) => {
668                let rule = arc.read_with(guard);
669                CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))
670            },
671            CssRule::Keyframes(ref arc) => {
672                let rule = arc.read_with(guard);
673                CssRule::Keyframes(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
674            },
675            CssRule::Margin(ref arc) => {
676                CssRule::Margin(Arc::new(arc.deep_clone_with_lock(lock, guard)))
677            },
678            CssRule::Supports(ref arc) => {
679                CssRule::Supports(Arc::new(arc.deep_clone_with_lock(lock, guard)))
680            },
681            CssRule::Page(ref arc) => {
682                let rule = arc.read_with(guard);
683                CssRule::Page(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
684            },
685            CssRule::Property(ref arc) => {
686                // @property rules are immutable, so we don't need any of the `Locked`
687                // shenanigans, actually, and can just share the rule.
688                CssRule::Property(arc.clone())
689            },
690            CssRule::Document(ref arc) => {
691                CssRule::Document(Arc::new(arc.deep_clone_with_lock(lock, guard)))
692            },
693            CssRule::LayerStatement(ref arc) => CssRule::LayerStatement(arc.clone()),
694            CssRule::LayerBlock(ref arc) => {
695                CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard)))
696            },
697            CssRule::Scope(ref arc) => {
698                CssRule::Scope(Arc::new(arc.deep_clone_with_lock(lock, guard)))
699            },
700            CssRule::StartingStyle(ref arc) => {
701                CssRule::StartingStyle(Arc::new(arc.deep_clone_with_lock(lock, guard)))
702            },
703            CssRule::PositionTry(ref arc) => {
704                let rule = arc.read_with(guard);
705                CssRule::PositionTry(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
706            },
707            CssRule::NestedDeclarations(ref arc) => {
708                let decls = arc.read_with(guard);
709                CssRule::NestedDeclarations(Arc::new(lock.wrap(decls.clone())))
710            },
711        }
712    }
713}
714
715impl ToCssWithGuard for CssRule {
716    // https://drafts.csswg.org/cssom/#serialize-a-css-rule
717    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
718        match *self {
719            CssRule::Namespace(ref rule) => rule.to_css(guard, dest),
720            CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),
721            CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),
722            CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),
723            CssRule::FontFeatureValues(ref rule) => rule.to_css(guard, dest),
724            CssRule::FontPaletteValues(ref rule) => rule.to_css(guard, dest),
725            CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest),
726            CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
727            CssRule::Margin(ref rule) => rule.to_css(guard, dest),
728            CssRule::Media(ref rule) => rule.to_css(guard, dest),
729            CssRule::Supports(ref rule) => rule.to_css(guard, dest),
730            CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
731            CssRule::Property(ref rule) => rule.to_css(guard, dest),
732            CssRule::Document(ref rule) => rule.to_css(guard, dest),
733            CssRule::LayerBlock(ref rule) => rule.to_css(guard, dest),
734            CssRule::LayerStatement(ref rule) => rule.to_css(guard, dest),
735            CssRule::Container(ref rule) => rule.to_css(guard, dest),
736            CssRule::Scope(ref rule) => rule.to_css(guard, dest),
737            CssRule::StartingStyle(ref rule) => rule.to_css(guard, dest),
738            CssRule::PositionTry(ref lock) => lock.read_with(guard).to_css(guard, dest),
739            CssRule::NestedDeclarations(ref lock) => lock.read_with(guard).to_css(guard, dest),
740        }
741    }
742}