1use std::cell::{Cell, Ref};
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::rust::HandleObject;
10use script_bindings::inheritance::Castable;
11use script_bindings::realms::InRealm;
12use script_bindings::root::Dom;
13use servo_arc::Arc;
14use style::media_queries::MediaList as StyleMediaList;
15use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
16use style::stylesheets::{
17 AllowImportRules, CssRuleTypes, Origin, Stylesheet as StyleStyleSheet, StylesheetContents,
18 StylesheetInDocument, UrlExtraData,
19};
20
21use super::cssrulelist::{CSSRuleList, RulesSource};
22use super::stylesheet::StyleSheet;
23use super::stylesheetlist::StyleSheetListOwner;
24use crate::dom::bindings::cell::DomRefCell;
25use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::{
26 CSSStyleSheetInit, CSSStyleSheetMethods,
27};
28use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
29use crate::dom::bindings::codegen::GenericBindings::CSSRuleListBinding::CSSRuleList_Binding::CSSRuleListMethods;
30use crate::dom::bindings::codegen::UnionTypes::MediaListOrString;
31use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
32use crate::dom::bindings::refcounted::Trusted;
33use crate::dom::bindings::reflector::{
34 DomGlobal, reflect_dom_object, reflect_dom_object_with_proto,
35};
36use crate::dom::bindings::root::{DomRoot, MutNullableDom};
37use crate::dom::bindings::str::{DOMString, USVString};
38use crate::dom::document::Document;
39use crate::dom::element::Element;
40use crate::dom::html::htmlstyleelement::HTMLStyleElement;
41use crate::dom::medialist::MediaList;
42use crate::dom::node::NodeTraits;
43use crate::dom::types::Promise;
44use crate::dom::window::Window;
45use crate::script_runtime::CanGc;
46use crate::test::TrustedPromise;
47
48#[dom_struct]
49pub(crate) struct CSSStyleSheet {
50 stylesheet: StyleSheet,
51
52 owner_node: MutNullableDom<Element>,
54
55 rulelist: MutNullableDom<CSSRuleList>,
57
58 #[ignore_malloc_size_of = "Stylo"]
60 #[no_trace]
61 style_stylesheet: DomRefCell<Arc<StyleStyleSheet>>,
62
63 #[no_trace]
66 style_shared_lock: SharedRwLock,
67
68 origin_clean: Cell<bool>,
70
71 constructor_document: Option<Dom<Document>>,
75
76 disallow_modification: Cell<bool>,
78
79 adopters: DomRefCell<Vec<StyleSheetListOwner>>,
82}
83
84impl CSSStyleSheet {
85 fn new_inherited(
86 owner: Option<&Element>,
87 type_: DOMString,
88 href: Option<DOMString>,
89 title: Option<DOMString>,
90 stylesheet: Arc<StyleStyleSheet>,
91 constructor_document: Option<&Document>,
92 ) -> CSSStyleSheet {
93 CSSStyleSheet {
94 stylesheet: StyleSheet::new_inherited(type_, href, title),
95 owner_node: MutNullableDom::new(owner),
96 rulelist: MutNullableDom::new(None),
97 style_shared_lock: stylesheet.shared_lock.clone(),
98 style_stylesheet: DomRefCell::new(stylesheet),
99 origin_clean: Cell::new(true),
100 constructor_document: constructor_document.map(Dom::from_ref),
101 adopters: Default::default(),
102 disallow_modification: Cell::new(false),
103 }
104 }
105
106 #[allow(clippy::too_many_arguments)]
107 pub(crate) fn new(
108 window: &Window,
109 owner: Option<&Element>,
110 type_: DOMString,
111 href: Option<DOMString>,
112 title: Option<DOMString>,
113 stylesheet: Arc<StyleStyleSheet>,
114 constructor_document: Option<&Document>,
115 can_gc: CanGc,
116 ) -> DomRoot<CSSStyleSheet> {
117 reflect_dom_object(
118 Box::new(CSSStyleSheet::new_inherited(
119 owner,
120 type_,
121 href,
122 title,
123 stylesheet,
124 constructor_document,
125 )),
126 window,
127 can_gc,
128 )
129 }
130
131 #[allow(clippy::too_many_arguments)]
132 fn new_with_proto(
133 window: &Window,
134 proto: Option<HandleObject>,
135 owner: Option<&Element>,
136 type_: DOMString,
137 href: Option<DOMString>,
138 title: Option<DOMString>,
139 stylesheet: Arc<StyleStyleSheet>,
140 constructor_document: Option<&Document>,
141 can_gc: CanGc,
142 ) -> DomRoot<CSSStyleSheet> {
143 reflect_dom_object_with_proto(
144 Box::new(CSSStyleSheet::new_inherited(
145 owner,
146 type_,
147 href,
148 title,
149 stylesheet,
150 constructor_document,
151 )),
152 window,
153 proto,
154 can_gc,
155 )
156 }
157
158 fn rulelist(&self, can_gc: CanGc) -> DomRoot<CSSRuleList> {
159 self.rulelist.or_init(|| {
160 let sheet = self.style_stylesheet.borrow();
161 let guard = sheet.shared_lock.read();
162 let rules = sheet.contents(&guard).rules.clone();
163 CSSRuleList::new(
164 self.global().as_window(),
165 self,
166 RulesSource::Rules(rules),
167 can_gc,
168 )
169 })
170 }
171
172 pub(crate) fn disabled(&self) -> bool {
173 self.style_stylesheet.borrow().disabled()
174 }
175
176 pub(crate) fn owner_node(&self) -> Option<DomRoot<Element>> {
177 self.owner_node.get()
178 }
179
180 pub(crate) fn set_disabled(&self, disabled: bool) {
181 if self.style_stylesheet.borrow().set_disabled(disabled) {
182 self.notify_invalidations();
183 }
184 }
185
186 pub(crate) fn set_owner_node(&self, value: Option<&Element>) {
187 self.owner_node.set(value);
188 }
189
190 pub(crate) fn shared_lock(&self) -> &SharedRwLock {
191 &self.style_shared_lock
192 }
193
194 pub(crate) fn style_stylesheet(&self) -> Ref<'_, Arc<StyleStyleSheet>> {
195 self.style_stylesheet.borrow()
196 }
197
198 pub(crate) fn set_origin_clean(&self, origin_clean: bool) {
199 self.origin_clean.set(origin_clean);
200 }
201
202 pub(crate) fn medialist(&self, can_gc: CanGc) -> DomRoot<MediaList> {
203 MediaList::new(
204 self.global().as_window(),
205 self,
206 self.style_stylesheet().media.clone(),
207 can_gc,
208 )
209 }
210
211 #[inline]
213 pub(crate) fn is_constructed(&self) -> bool {
214 self.constructor_document.is_some()
215 }
216
217 pub(crate) fn constructor_document_matches(&self, other_doc: &Document) -> bool {
218 match &self.constructor_document {
219 Some(doc) => *doc == other_doc,
220 None => false,
221 }
222 }
223
224 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
227 pub(crate) fn add_adopter(&self, owner: StyleSheetListOwner) {
228 debug_assert!(self.is_constructed());
229 self.adopters.borrow_mut().push(owner);
230 }
231
232 pub(crate) fn remove_adopter(&self, owner: &StyleSheetListOwner) {
233 let adopters = &mut *self.adopters.borrow_mut();
234 if let Some(index) = adopters.iter().position(|o| o == owner) {
235 adopters.swap_remove(index);
236 }
237 }
238
239 pub(crate) fn will_modify(&self) {
240 let Some(node) = self.owner_node.get() else {
241 return;
242 };
243
244 let Some(node) = node.downcast::<HTMLStyleElement>() else {
245 return;
246 };
247
248 node.will_modify_stylesheet();
249 }
250
251 pub(crate) fn update_style_stylesheet(
252 &self,
253 style_stylesheet: &Arc<StyleStyleSheet>,
254 guard: &SharedRwLockReadGuard,
255 ) {
256 *self.style_stylesheet.borrow_mut() = style_stylesheet.clone();
263 if let Some(rulelist) = self.rulelist.get() {
264 let rules = style_stylesheet.contents(guard).rules.clone();
265 rulelist.update_rules(RulesSource::Rules(rules), guard);
266 }
267 }
268
269 pub(crate) fn notify_invalidations(&self) {
271 if let Some(owner) = self.owner_node() {
272 owner.stylesheet_list_owner().invalidate_stylesheets();
273 }
274 for adopter in self.adopters.borrow().iter() {
275 adopter.invalidate_stylesheets();
276 }
277 }
278
279 pub(crate) fn disallow_modification(&self) -> bool {
281 self.disallow_modification.get()
282 }
283
284 fn do_replace_sync(&self, text: USVString) {
286 let global = self.global();
288 let window = global.as_window();
289
290 self.will_modify();
291
292 let _span = profile_traits::trace_span!("ParseStylesheet").entered();
293 let sheet = self.style_stylesheet();
294 let new_contents = StylesheetContents::from_str(
295 &text,
296 UrlExtraData(window.get_url().get_arc()),
297 Origin::Author,
298 &self.style_shared_lock,
299 None,
300 Some(window.css_error_reporter()),
301 window.Document().quirks_mode(),
302 AllowImportRules::No, None,
304 );
305
306 {
307 let mut write_guard = self.style_shared_lock.write();
308 *sheet.contents.write_with(&mut write_guard) = new_contents;
309 }
310
311 self.rulelist.set(None);
315
316 self.notify_invalidations();
318 }
319}
320
321impl CSSStyleSheetMethods<crate::DomTypeHolder> for CSSStyleSheet {
322 fn Constructor(
324 window: &Window,
325 proto: Option<HandleObject>,
326 can_gc: CanGc,
327 options: &CSSStyleSheetInit,
328 ) -> DomRoot<Self> {
329 let doc = window.Document();
330 let shared_lock = doc.style_shared_lock().clone();
331 let media = Arc::new(shared_lock.wrap(match &options.media {
332 Some(media) => match media {
333 MediaListOrString::MediaList(media_list) => media_list.clone_media_list(),
334 MediaListOrString::String(str) => MediaList::parse_media_list(&str.str(), window),
335 },
336 None => StyleMediaList::empty(),
337 }));
338 let stylesheet = Arc::new(StyleStyleSheet::from_str(
339 "",
340 UrlExtraData(window.get_url().get_arc()),
341 Origin::Author,
342 media,
343 shared_lock,
344 None,
345 Some(window.css_error_reporter()),
346 doc.quirks_mode(),
347 AllowImportRules::No,
348 ));
349 if options.disabled {
350 stylesheet.set_disabled(true);
351 }
352 Self::new_with_proto(
353 window,
354 proto,
355 None, "text/css".into(),
357 None, None, stylesheet,
360 Some(&window.Document()), can_gc,
362 )
363 }
364
365 fn GetCssRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> {
367 if !self.origin_clean.get() {
368 return Err(Error::Security(None));
369 }
370 Ok(self.rulelist(can_gc))
371 }
372
373 fn InsertRule(&self, rule: DOMString, index: u32, can_gc: CanGc) -> Fallible<u32> {
375 if !self.origin_clean.get() {
377 return Err(Error::Security(None));
378 }
379
380 if self.disallow_modification() {
382 return Err(Error::NotAllowed(None));
383 }
384
385 self.rulelist(can_gc)
386 .insert_rule(&rule, index, CssRuleTypes::default(), None, can_gc)
387 }
388
389 fn DeleteRule(&self, index: u32, can_gc: CanGc) -> ErrorResult {
391 if !self.origin_clean.get() {
393 return Err(Error::Security(None));
394 }
395
396 if self.disallow_modification() {
398 return Err(Error::NotAllowed(None));
399 }
400 self.rulelist(can_gc).remove_rule(index)
401 }
402
403 fn GetRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> {
405 self.GetCssRules(can_gc)
406 }
407
408 fn RemoveRule(&self, index: u32, can_gc: CanGc) -> ErrorResult {
410 self.DeleteRule(index, can_gc)
411 }
412
413 fn AddRule(
415 &self,
416 selector: DOMString,
417 block: DOMString,
418 optional_index: Option<u32>,
419 can_gc: CanGc,
420 ) -> Fallible<i32> {
421 let mut rule = selector;
424
425 if block.is_empty() {
429 rule.push_str(" { }");
430 } else {
431 rule.push_str(" { ");
432 rule.push_str(&block.str());
433 rule.push_str(" }");
434 };
435
436 let index = optional_index.unwrap_or_else(|| self.rulelist(can_gc).Length());
438
439 self.InsertRule(rule, index, can_gc)?;
441
442 Ok(-1)
444 }
445
446 fn Replace(&self, text: USVString, comp: InRealm, can_gc: CanGc) -> Fallible<Rc<Promise>> {
448 let promise = Promise::new_in_current_realm(comp, can_gc);
450
451 if !self.is_constructed() || self.disallow_modification() {
454 return Err(Error::NotAllowed(None));
455 }
456
457 self.disallow_modification.set(true);
459
460 let trusted_sheet = Trusted::new(self);
462 let trusted_promise = TrustedPromise::new(promise.clone());
463
464 self.global()
465 .task_manager()
466 .dom_manipulation_task_source()
467 .queue(task!(cssstylesheet_replace: move || {
468 let sheet = trusted_sheet.root();
469
470 sheet.do_replace_sync(text);
472
473 sheet.disallow_modification.set(false);
475
476 trusted_promise.root().resolve_native(&sheet, CanGc::note());
478 }));
479
480 Ok(promise)
481 }
482
483 fn ReplaceSync(&self, text: USVString) -> Result<(), Error> {
485 if !self.is_constructed() || self.disallow_modification() {
488 return Err(Error::NotAllowed(None));
489 }
490 self.do_replace_sync(text);
491 Ok(())
492 }
493}