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