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