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