script/dom/css/
cssrule.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
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
9use style::stylesheets::{CssRule as StyleCssRule, CssRuleType};
10
11use super::cssfontfacerule::CSSFontFaceRule;
12use super::cssimportrule::CSSImportRule;
13use super::csskeyframerule::CSSKeyframeRule;
14use super::csskeyframesrule::CSSKeyframesRule;
15use super::csslayerblockrule::CSSLayerBlockRule;
16use super::csslayerstatementrule::CSSLayerStatementRule;
17use super::cssmediarule::CSSMediaRule;
18use super::cssnamespacerule::CSSNamespaceRule;
19use super::cssnesteddeclarations::CSSNestedDeclarations;
20use super::cssstylerule::CSSStyleRule;
21use super::cssstylesheet::CSSStyleSheet;
22use super::csssupportsrule::CSSSupportsRule;
23use crate::dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleMethods;
24use crate::dom::bindings::inheritance::Castable;
25use crate::dom::bindings::reflector::Reflector;
26use crate::dom::bindings::root::{Dom, DomRoot};
27use crate::dom::bindings::str::DOMString;
28use crate::dom::window::Window;
29use crate::script_runtime::CanGc;
30
31#[dom_struct]
32pub(crate) struct CSSRule {
33    reflector_: Reflector,
34    parent_stylesheet: Dom<CSSStyleSheet>,
35
36    /// Whether the parentStyleSheet attribute should return null.
37    /// We keep parent_stylesheet in that case because insertRule needs it
38    /// for the stylesheet’s base URL and namespace prefixes.
39    parent_stylesheet_removed: Cell<bool>,
40}
41
42impl CSSRule {
43    pub(crate) fn new_inherited(parent_stylesheet: &CSSStyleSheet) -> CSSRule {
44        CSSRule {
45            reflector_: Reflector::new(),
46            parent_stylesheet: Dom::from_ref(parent_stylesheet),
47            parent_stylesheet_removed: Cell::new(false),
48        }
49    }
50
51    pub(crate) fn as_specific(&self) -> &dyn SpecificCSSRule {
52        if let Some(rule) = self.downcast::<CSSStyleRule>() {
53            rule as &dyn SpecificCSSRule
54        } else if let Some(rule) = self.downcast::<CSSFontFaceRule>() {
55            rule as &dyn SpecificCSSRule
56        } else if let Some(rule) = self.downcast::<CSSKeyframesRule>() {
57            rule as &dyn SpecificCSSRule
58        } else if let Some(rule) = self.downcast::<CSSMediaRule>() {
59            rule as &dyn SpecificCSSRule
60        } else if let Some(rule) = self.downcast::<CSSNamespaceRule>() {
61            rule as &dyn SpecificCSSRule
62        } else if let Some(rule) = self.downcast::<CSSKeyframeRule>() {
63            rule as &dyn SpecificCSSRule
64        } else if let Some(rule) = self.downcast::<CSSImportRule>() {
65            rule as &dyn SpecificCSSRule
66        } else if let Some(rule) = self.downcast::<CSSSupportsRule>() {
67            rule as &dyn SpecificCSSRule
68        } else if let Some(rule) = self.downcast::<CSSLayerBlockRule>() {
69            rule as &dyn SpecificCSSRule
70        } else if let Some(rule) = self.downcast::<CSSLayerStatementRule>() {
71            rule as &dyn SpecificCSSRule
72        } else if let Some(rule) = self.downcast::<CSSNestedDeclarations>() {
73            rule as &dyn SpecificCSSRule
74        } else {
75            unreachable!()
76        }
77    }
78
79    // Given a StyleCssRule, create a new instance of a derived class of
80    // CSSRule based on which rule it is
81    pub(crate) fn new_specific(
82        window: &Window,
83        parent_stylesheet: &CSSStyleSheet,
84        rule: StyleCssRule,
85        can_gc: CanGc,
86    ) -> DomRoot<CSSRule> {
87        // be sure to update the match in as_specific when this is updated
88        match rule {
89            StyleCssRule::Import(s) => {
90                DomRoot::upcast(CSSImportRule::new(window, parent_stylesheet, s, can_gc))
91            },
92            StyleCssRule::Style(s) => {
93                DomRoot::upcast(CSSStyleRule::new(window, parent_stylesheet, s, can_gc))
94            },
95            StyleCssRule::FontFace(s) => {
96                DomRoot::upcast(CSSFontFaceRule::new(window, parent_stylesheet, s, can_gc))
97            },
98            StyleCssRule::FontFeatureValues(_) => unimplemented!(),
99            StyleCssRule::CounterStyle(_) => unimplemented!(),
100            StyleCssRule::Keyframes(s) => {
101                DomRoot::upcast(CSSKeyframesRule::new(window, parent_stylesheet, s, can_gc))
102            },
103            StyleCssRule::Media(s) => {
104                DomRoot::upcast(CSSMediaRule::new(window, parent_stylesheet, s, can_gc))
105            },
106            StyleCssRule::Namespace(s) => {
107                DomRoot::upcast(CSSNamespaceRule::new(window, parent_stylesheet, s, can_gc))
108            },
109            StyleCssRule::Supports(s) => {
110                DomRoot::upcast(CSSSupportsRule::new(window, parent_stylesheet, s, can_gc))
111            },
112            StyleCssRule::Page(_) => unreachable!(),
113            StyleCssRule::Container(_) => unimplemented!(), // TODO
114            StyleCssRule::Document(_) => unimplemented!(),  // TODO
115            StyleCssRule::LayerBlock(s) => {
116                DomRoot::upcast(CSSLayerBlockRule::new(window, parent_stylesheet, s, can_gc))
117            },
118            StyleCssRule::LayerStatement(s) => DomRoot::upcast(CSSLayerStatementRule::new(
119                window,
120                parent_stylesheet,
121                s,
122                can_gc,
123            )),
124            StyleCssRule::FontPaletteValues(_) => unimplemented!(), // TODO
125            StyleCssRule::Property(_) => unimplemented!(),          // TODO
126            StyleCssRule::Margin(_) => unimplemented!(),            // TODO
127            StyleCssRule::Scope(_) => unimplemented!(),             // TODO
128            StyleCssRule::StartingStyle(_) => unimplemented!(),     // TODO
129            StyleCssRule::PositionTry(_) => unimplemented!(),       // TODO
130            StyleCssRule::CustomMedia(_) => unimplemented!(),       // TODO
131            StyleCssRule::NestedDeclarations(s) => DomRoot::upcast(CSSNestedDeclarations::new(
132                window,
133                parent_stylesheet,
134                s,
135                can_gc,
136            )),
137        }
138    }
139
140    /// Sets owner sheet/rule to null
141    pub(crate) fn detach(&self) {
142        self.deparent();
143        // should set parent rule to None when we add parent rule support
144    }
145
146    /// Sets owner sheet to null (and does the same for all children)
147    pub(crate) fn deparent(&self) {
148        self.parent_stylesheet_removed.set(true);
149        // https://github.com/w3c/csswg-drafts/issues/722
150        // Spec doesn't ask us to do this, but it makes sense
151        // and browsers implement this behavior
152        self.as_specific().deparent_children();
153    }
154
155    pub(crate) fn parent_stylesheet(&self) -> &CSSStyleSheet {
156        &self.parent_stylesheet
157    }
158
159    pub(crate) fn shared_lock(&self) -> &SharedRwLock {
160        self.parent_stylesheet.shared_lock()
161    }
162
163    pub(crate) fn update_rule(&self, style_rule: &StyleCssRule, guard: &SharedRwLockReadGuard) {
164        match style_rule {
165            StyleCssRule::Import(s) => {
166                if let Some(rule) = self.downcast::<CSSImportRule>() {
167                    rule.update_rule(s.clone());
168                }
169            },
170            StyleCssRule::Style(s) => {
171                if let Some(rule) = self.downcast::<CSSStyleRule>() {
172                    rule.update_rule(s.clone(), guard);
173                }
174            },
175            StyleCssRule::FontFace(s) => {
176                if let Some(rule) = self.downcast::<CSSFontFaceRule>() {
177                    rule.update_rule(s.clone());
178                }
179            },
180            StyleCssRule::FontFeatureValues(_) => unimplemented!(),
181            StyleCssRule::CounterStyle(_) => unimplemented!(),
182            StyleCssRule::Keyframes(s) => {
183                if let Some(rule) = self.downcast::<CSSKeyframesRule>() {
184                    rule.update_rule(s.clone(), guard);
185                }
186            },
187            StyleCssRule::Media(s) => {
188                if let Some(rule) = self.downcast::<CSSMediaRule>() {
189                    rule.update_rule(s.clone(), guard);
190                }
191            },
192            StyleCssRule::Namespace(s) => {
193                if let Some(rule) = self.downcast::<CSSNamespaceRule>() {
194                    rule.update_rule(s.clone());
195                }
196            },
197            StyleCssRule::Supports(s) => {
198                if let Some(rule) = self.downcast::<CSSSupportsRule>() {
199                    rule.update_rule(s.clone(), guard);
200                }
201            },
202            StyleCssRule::Page(_) => unreachable!(),
203            StyleCssRule::Container(_) => unimplemented!(), // TODO
204            StyleCssRule::Document(_) => unimplemented!(),  // TODO
205            StyleCssRule::LayerBlock(s) => {
206                if let Some(rule) = self.downcast::<CSSLayerBlockRule>() {
207                    rule.update_rule(s.clone(), guard);
208                }
209            },
210            StyleCssRule::LayerStatement(s) => {
211                if let Some(rule) = self.downcast::<CSSLayerStatementRule>() {
212                    rule.update_rule(s.clone());
213                }
214            },
215            StyleCssRule::FontPaletteValues(_) => unimplemented!(), // TODO
216            StyleCssRule::Property(_) => unimplemented!(),          // TODO
217            StyleCssRule::Margin(_) => unimplemented!(),            // TODO
218            StyleCssRule::Scope(_) => unimplemented!(),             // TODO
219            StyleCssRule::StartingStyle(_) => unimplemented!(),     // TODO
220            StyleCssRule::PositionTry(_) => unimplemented!(),       // TODO
221            StyleCssRule::CustomMedia(_) => unimplemented!(),       // TODO
222            StyleCssRule::NestedDeclarations(s) => {
223                if let Some(rule) = self.downcast::<CSSNestedDeclarations>() {
224                    rule.update_rule(s.clone(), guard);
225                }
226            },
227        }
228    }
229}
230
231impl CSSRuleMethods<crate::DomTypeHolder> for CSSRule {
232    /// <https://drafts.csswg.org/cssom/#dom-cssrule-type>
233    fn Type(&self) -> u16 {
234        let rule_type = self.as_specific().ty() as u16;
235        // Per https://drafts.csswg.org/cssom/#dom-cssrule-type for constants > 15
236        // we return 0.
237        if rule_type > 15 { 0 } else { rule_type }
238    }
239
240    /// <https://drafts.csswg.org/cssom/#dom-cssrule-parentstylesheet>
241    fn GetParentStyleSheet(&self) -> Option<DomRoot<CSSStyleSheet>> {
242        if self.parent_stylesheet_removed.get() {
243            None
244        } else {
245            Some(DomRoot::from_ref(&*self.parent_stylesheet))
246        }
247    }
248
249    /// <https://drafts.csswg.org/cssom/#dom-cssrule-csstext>
250    fn CssText(&self) -> DOMString {
251        self.as_specific().get_css()
252    }
253
254    /// <https://drafts.csswg.org/cssom/#dom-cssrule-csstext>
255    fn SetCssText(&self, _: DOMString) {
256        // do nothing
257    }
258}
259
260pub(crate) trait SpecificCSSRule {
261    fn ty(&self) -> CssRuleType;
262    fn get_css(&self) -> DOMString;
263    /// Remove parentStylesheet from all transitive children
264    fn deparent_children(&self) {
265        // most CSSRules do nothing here
266    }
267}