script/dom/css/
cssstylerule.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::RefCell;
6use std::mem;
7
8use cssparser::{Parser as CssParser, ParserInput as CssParserInput, ToCss};
9use dom_struct::dom_struct;
10use js::context::JSContext;
11use selectors::parser::{ParseRelative, SelectorList};
12use servo_arc::Arc;
13use style::selector_parser::SelectorParser;
14use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard};
15use style::stylesheets::{CssRuleType, CssRules, Origin, StyleRule, StylesheetInDocument};
16
17use super::cssgroupingrule::CSSGroupingRule;
18use super::cssrule::SpecificCSSRule;
19use super::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
20use super::cssstylesheet::CSSStyleSheet;
21use crate::dom::bindings::codegen::Bindings::CSSStyleRuleBinding::CSSStyleRuleMethods;
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
24use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
25use crate::dom::bindings::str::DOMString;
26use crate::dom::window::Window;
27use crate::script_runtime::CanGc;
28
29#[dom_struct]
30pub(crate) struct CSSStyleRule {
31    css_grouping_rule: CSSGroupingRule,
32    #[ignore_malloc_size_of = "Stylo"]
33    #[no_trace]
34    style_rule: RefCell<Arc<Locked<StyleRule>>>,
35    style_declaration: MutNullableDom<CSSStyleDeclaration>,
36}
37
38impl CSSStyleRule {
39    fn new_inherited(
40        parent_stylesheet: &CSSStyleSheet,
41        stylerule: Arc<Locked<StyleRule>>,
42    ) -> CSSStyleRule {
43        CSSStyleRule {
44            css_grouping_rule: CSSGroupingRule::new_inherited(parent_stylesheet),
45            style_rule: RefCell::new(stylerule),
46            style_declaration: Default::default(),
47        }
48    }
49
50    pub(crate) fn new(
51        window: &Window,
52        parent_stylesheet: &CSSStyleSheet,
53        stylerule: Arc<Locked<StyleRule>>,
54        can_gc: CanGc,
55    ) -> DomRoot<CSSStyleRule> {
56        reflect_dom_object(
57            Box::new(CSSStyleRule::new_inherited(parent_stylesheet, stylerule)),
58            window,
59            can_gc,
60        )
61    }
62
63    pub(crate) fn ensure_rules(&self) -> Arc<Locked<CssRules>> {
64        let lock = self.css_grouping_rule.shared_lock();
65        let mut guard = lock.write();
66        self.style_rule
67            .borrow()
68            .write_with(&mut guard)
69            .rules
70            .get_or_insert_with(|| CssRules::new(vec![], lock))
71            .clone()
72    }
73
74    pub(crate) fn update_rule(
75        &self,
76        stylerule: Arc<Locked<StyleRule>>,
77        guard: &SharedRwLockReadGuard,
78    ) {
79        if let Some(ref rules) = stylerule.read_with(guard).rules {
80            self.css_grouping_rule.update_rules(rules, guard);
81        }
82
83        if let Some(ref style_decl) = self.style_declaration.get() {
84            style_decl.update_property_declaration_block(&stylerule.read_with(guard).block);
85        }
86
87        *self.style_rule.borrow_mut() = stylerule;
88    }
89
90    pub(crate) fn block_id(&self) -> usize {
91        let guard = self.css_grouping_rule.shared_lock().read();
92        self.style_rule
93            .borrow()
94            .read_with(&guard)
95            .block
96            .raw_ptr()
97            .as_ptr() as usize
98    }
99}
100
101impl SpecificCSSRule for CSSStyleRule {
102    fn ty(&self) -> CssRuleType {
103        CssRuleType::Style
104    }
105
106    fn get_css(&self) -> DOMString {
107        let guard = self.css_grouping_rule.shared_lock().read();
108        self.style_rule
109            .borrow()
110            .read_with(&guard)
111            .to_css_string(&guard)
112            .into()
113    }
114}
115
116impl CSSStyleRuleMethods<crate::DomTypeHolder> for CSSStyleRule {
117    /// <https://drafts.csswg.org/cssom/#dom-cssstylerule-style>
118    fn Style(&self, cx: &mut JSContext) -> DomRoot<CSSStyleDeclaration> {
119        self.style_declaration.or_init(|| {
120            let guard = self.css_grouping_rule.shared_lock().read();
121            CSSStyleDeclaration::new(
122                self.global().as_window(),
123                CSSStyleOwner::CSSRule(
124                    Dom::from_ref(self.upcast()),
125                    RefCell::new(self.style_rule.borrow().read_with(&guard).block.clone()),
126                ),
127                None,
128                CSSModificationAccess::ReadWrite,
129                CanGc::from_cx(cx),
130            )
131        })
132    }
133
134    /// <https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext>
135    fn SelectorText(&self) -> DOMString {
136        let guard = self.css_grouping_rule.shared_lock().read();
137        self.style_rule
138            .borrow()
139            .read_with(&guard)
140            .selectors
141            .to_css_string()
142            .into()
143    }
144
145    /// <https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext>
146    fn SetSelectorText(&self, value: DOMString) {
147        let value = value.str();
148        let Ok(mut selector) = ({
149            let guard = self.css_grouping_rule.shared_lock().read();
150            let sheet = self
151                .css_grouping_rule
152                .parent_stylesheet()
153                .style_stylesheet();
154            let contents = sheet.contents(&guard);
155            // It's not clear from the spec if we should use the stylesheet's namespaces.
156            // https://github.com/w3c/csswg-drafts/issues/1511
157            let parser = SelectorParser {
158                stylesheet_origin: Origin::Author,
159                namespaces: &contents.namespaces,
160                url_data: &contents.url_data,
161                for_supports_rule: false,
162            };
163            let mut css_parser = CssParserInput::new(&value);
164            let mut css_parser = CssParser::new(&mut css_parser);
165            // TODO: Maybe allow setting relative selectors from the OM, if we're in a nested style
166            // rule?
167            SelectorList::parse(&parser, &mut css_parser, ParseRelative::No)
168        }) else {
169            return;
170        };
171        self.css_grouping_rule.parent_stylesheet().will_modify();
172        // This mirrors what we do in CSSStyleOwner::mutate_associated_block.
173        let mut guard = self.css_grouping_rule.shared_lock().write();
174        mem::swap(
175            &mut self.style_rule.borrow().write_with(&mut guard).selectors,
176            &mut selector,
177        );
178        self.css_grouping_rule
179            .parent_stylesheet()
180            .notify_invalidations();
181    }
182}