Skip to main content

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