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