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