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