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