Skip to main content

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