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, UrlExtraData,
18};
19
20use crate::dom::bindings::cell::DomRefCell;
21use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::{
22 CSSStyleSheetInit, CSSStyleSheetMethods,
23};
24use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
25use crate::dom::bindings::codegen::GenericBindings::CSSRuleListBinding::CSSRuleList_Binding::CSSRuleListMethods;
26use crate::dom::bindings::codegen::UnionTypes::MediaListOrString;
27use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
28use crate::dom::bindings::refcounted::Trusted;
29use crate::dom::bindings::reflector::{
30 DomGlobal, reflect_dom_object, reflect_dom_object_with_proto,
31};
32use crate::dom::bindings::root::{DomRoot, MutNullableDom};
33use crate::dom::bindings::str::{DOMString, USVString};
34use crate::dom::cssrulelist::{CSSRuleList, RulesSource};
35use crate::dom::document::Document;
36use crate::dom::element::Element;
37use crate::dom::html::htmlstyleelement::HTMLStyleElement;
38use crate::dom::medialist::MediaList;
39use crate::dom::node::NodeTraits;
40use crate::dom::stylesheet::StyleSheet;
41use crate::dom::stylesheetlist::StyleSheetListOwner;
42use crate::dom::types::Promise;
43use crate::dom::window::Window;
44use crate::script_runtime::CanGc;
45use crate::test::TrustedPromise;
46
47#[dom_struct]
48pub(crate) struct CSSStyleSheet {
49 stylesheet: StyleSheet,
50
51 owner_node: MutNullableDom<Element>,
53
54 rulelist: MutNullableDom<CSSRuleList>,
56
57 #[ignore_malloc_size_of = "Arc"]
59 #[no_trace]
60 style_stylesheet: DomRefCell<Arc<StyleStyleSheet>>,
61
62 #[no_trace]
65 style_shared_lock: SharedRwLock,
66
67 origin_clean: Cell<bool>,
69
70 constructor_document: Option<Dom<Document>>,
74
75 disallow_modification: Cell<bool>,
77
78 adopters: DomRefCell<Vec<StyleSheetListOwner>>,
81}
82
83impl CSSStyleSheet {
84 fn new_inherited(
85 owner: Option<&Element>,
86 type_: DOMString,
87 href: Option<DOMString>,
88 title: Option<DOMString>,
89 stylesheet: Arc<StyleStyleSheet>,
90 constructor_document: Option<&Document>,
91 ) -> CSSStyleSheet {
92 CSSStyleSheet {
93 stylesheet: StyleSheet::new_inherited(type_, href, title),
94 owner_node: MutNullableDom::new(owner),
95 rulelist: MutNullableDom::new(None),
96 style_shared_lock: stylesheet.shared_lock.clone(),
97 style_stylesheet: DomRefCell::new(stylesheet),
98 origin_clean: Cell::new(true),
99 constructor_document: constructor_document.map(Dom::from_ref),
100 adopters: Default::default(),
101 disallow_modification: Cell::new(false),
102 }
103 }
104
105 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
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 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
132 #[allow(clippy::too_many_arguments)]
133 fn new_with_proto(
134 window: &Window,
135 proto: Option<HandleObject>,
136 owner: Option<&Element>,
137 type_: DOMString,
138 href: Option<DOMString>,
139 title: Option<DOMString>,
140 stylesheet: Arc<StyleStyleSheet>,
141 constructor_document: Option<&Document>,
142 can_gc: CanGc,
143 ) -> DomRoot<CSSStyleSheet> {
144 reflect_dom_object_with_proto(
145 Box::new(CSSStyleSheet::new_inherited(
146 owner,
147 type_,
148 href,
149 title,
150 stylesheet,
151 constructor_document,
152 )),
153 window,
154 proto,
155 can_gc,
156 )
157 }
158
159 fn rulelist(&self, can_gc: CanGc) -> DomRoot<CSSRuleList> {
160 self.rulelist.or_init(|| {
161 let rules = self.style_stylesheet.borrow().contents.rules.clone();
162 CSSRuleList::new(
163 self.global().as_window(),
164 self,
165 RulesSource::Rules(rules),
166 can_gc,
167 )
168 })
169 }
170
171 pub(crate) fn disabled(&self) -> bool {
172 self.style_stylesheet.borrow().disabled()
173 }
174
175 pub(crate) fn owner_node(&self) -> Option<DomRoot<Element>> {
176 self.owner_node.get()
177 }
178
179 pub(crate) fn set_disabled(&self, disabled: bool) {
180 if self.style_stylesheet.borrow().set_disabled(disabled) {
181 self.notify_invalidations();
182 }
183 }
184
185 pub(crate) fn set_owner_node(&self, value: Option<&Element>) {
186 self.owner_node.set(value);
187 }
188
189 pub(crate) fn shared_lock(&self) -> &SharedRwLock {
190 &self.style_shared_lock
191 }
192
193 pub(crate) fn style_stylesheet(&self) -> Ref<'_, Arc<StyleStyleSheet>> {
194 self.style_stylesheet.borrow()
195 }
196
197 pub(crate) fn set_origin_clean(&self, origin_clean: bool) {
198 self.origin_clean.set(origin_clean);
199 }
200
201 pub(crate) fn medialist(&self, can_gc: CanGc) -> DomRoot<MediaList> {
202 MediaList::new(
203 self.global().as_window(),
204 self,
205 self.style_stylesheet().media.clone(),
206 can_gc,
207 )
208 }
209
210 #[inline]
212 pub(crate) fn is_constructed(&self) -> bool {
213 self.constructor_document.is_some()
214 }
215
216 pub(crate) fn constructor_document_matches(&self, other_doc: &Document) -> bool {
217 match &self.constructor_document {
218 Some(doc) => *doc == other_doc,
219 None => false,
220 }
221 }
222
223 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
226 pub(crate) fn add_adopter(&self, owner: StyleSheetListOwner) {
227 debug_assert!(self.is_constructed());
228 self.adopters.borrow_mut().push(owner);
229 }
230
231 pub(crate) fn remove_adopter(&self, owner: &StyleSheetListOwner) {
232 let adopters = &mut *self.adopters.borrow_mut();
233 if let Some(index) = adopters.iter().position(|o| o == owner) {
234 adopters.swap_remove(index);
235 }
236 }
237
238 pub(crate) fn will_modify(&self) {
239 let Some(node) = self.owner_node.get() else {
240 return;
241 };
242
243 let Some(node) = node.downcast::<HTMLStyleElement>() else {
244 return;
245 };
246
247 node.will_modify_stylesheet();
248 }
249
250 pub(crate) fn update_style_stylesheet(
251 &self,
252 style_stylesheet: &Arc<StyleStyleSheet>,
253 guard: &SharedRwLockReadGuard,
254 ) {
255 *self.style_stylesheet.borrow_mut() = style_stylesheet.clone();
262 if let Some(rulelist) = self.rulelist.get() {
263 let rules = style_stylesheet.contents.rules.clone();
264 rulelist.update_rules(RulesSource::Rules(rules), guard);
265 }
266 }
267
268 pub(crate) fn notify_invalidations(&self) {
270 if let Some(owner) = self.owner_node() {
271 owner.stylesheet_list_owner().invalidate_stylesheets();
272 }
273 for adopter in self.adopters.borrow().iter() {
274 adopter.invalidate_stylesheets();
275 }
276 }
277
278 pub(crate) fn disallow_modification(&self) -> bool {
280 self.disallow_modification.get()
281 }
282}
283
284impl CSSStyleSheetMethods<crate::DomTypeHolder> for CSSStyleSheet {
285 fn Constructor(
287 window: &Window,
288 proto: Option<HandleObject>,
289 can_gc: CanGc,
290 options: &CSSStyleSheetInit,
291 ) -> DomRoot<Self> {
292 let doc = window.Document();
293 let shared_lock = doc.style_shared_lock().clone();
294 let media = Arc::new(shared_lock.wrap(match &options.media {
295 Some(media) => match media {
296 MediaListOrString::MediaList(media_list) => media_list.clone_media_list(),
297 MediaListOrString::String(str) => MediaList::parse_media_list(str, window),
298 },
299 None => StyleMediaList::empty(),
300 }));
301 let stylesheet = Arc::new(StyleStyleSheet::from_str(
302 "",
303 UrlExtraData(window.get_url().get_arc()),
304 Origin::Author,
305 media,
306 shared_lock,
307 None,
308 window.css_error_reporter(),
309 doc.quirks_mode(),
310 AllowImportRules::No,
311 ));
312 if options.disabled {
313 stylesheet.set_disabled(true);
314 }
315 Self::new_with_proto(
316 window,
317 proto,
318 None, "text/css".into(),
320 None, None, stylesheet,
323 Some(&window.Document()), can_gc,
325 )
326 }
327
328 fn GetCssRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> {
330 if !self.origin_clean.get() {
331 return Err(Error::Security);
332 }
333 Ok(self.rulelist(can_gc))
334 }
335
336 fn InsertRule(&self, rule: DOMString, index: u32, can_gc: CanGc) -> Fallible<u32> {
338 if !self.origin_clean.get() {
340 return Err(Error::Security);
341 }
342
343 if self.disallow_modification() {
345 return Err(Error::NotAllowed);
346 }
347
348 self.rulelist(can_gc)
349 .insert_rule(&rule, index, CssRuleTypes::default(), None, can_gc)
350 }
351
352 fn DeleteRule(&self, index: u32, can_gc: CanGc) -> ErrorResult {
354 if !self.origin_clean.get() {
356 return Err(Error::Security);
357 }
358
359 if self.disallow_modification() {
361 return Err(Error::NotAllowed);
362 }
363 self.rulelist(can_gc).remove_rule(index)
364 }
365
366 fn GetRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> {
368 self.GetCssRules(can_gc)
369 }
370
371 fn RemoveRule(&self, index: u32, can_gc: CanGc) -> ErrorResult {
373 self.DeleteRule(index, can_gc)
374 }
375
376 fn AddRule(
378 &self,
379 selector: DOMString,
380 block: DOMString,
381 optional_index: Option<u32>,
382 can_gc: CanGc,
383 ) -> Fallible<i32> {
384 let mut rule = selector;
387
388 if block.is_empty() {
392 rule.push_str(" { }");
393 } else {
394 rule.push_str(" { ");
395 rule.push_str(block.str());
396 rule.push_str(" }");
397 };
398
399 let index = optional_index.unwrap_or_else(|| self.rulelist(can_gc).Length());
401
402 self.InsertRule(rule, index, can_gc)?;
404
405 Ok(-1)
407 }
408
409 fn Replace(&self, text: USVString, comp: InRealm, can_gc: CanGc) -> Fallible<Rc<Promise>> {
411 let promise = Promise::new_in_current_realm(comp, can_gc);
413
414 if !self.is_constructed() || self.disallow_modification() {
417 return Err(Error::NotAllowed);
418 }
419
420 self.disallow_modification.set(true);
422
423 let trusted_sheet = Trusted::new(self);
425 let trusted_promise = TrustedPromise::new(promise.clone());
426
427 self.global()
428 .task_manager()
429 .dom_manipulation_task_source()
430 .queue(task!(cssstylesheet_replace: move || {
431 let sheet = trusted_sheet.root();
432
433 let global = sheet.global();
437 let window = global.as_window();
438
439 sheet.will_modify();
440
441 #[cfg(feature = "tracing")]
442 let _span = tracing::trace_span!("ParseStylesheet", servo_profiling = true).entered();
443 StyleStyleSheet::update_from_str(
444 &sheet.style_stylesheet(),
445 &text,
446 UrlExtraData(window.get_url().get_arc()),
447 None,
448 window.css_error_reporter(),
449 AllowImportRules::No,
450 );
451
452 sheet.rulelist.set(None);
456
457 sheet.notify_invalidations();
459
460 sheet.disallow_modification.set(false);
462
463 trusted_promise.root().resolve_native(&sheet, CanGc::note());
465 }));
466
467 Ok(promise)
468 }
469
470 fn ReplaceSync(&self, text: USVString) -> Result<(), Error> {
472 if !self.is_constructed() || self.disallow_modification() {
475 return Err(Error::NotAllowed);
476 }
477
478 let global = self.global();
480 let window = global.as_window();
481
482 self.will_modify();
483
484 #[cfg(feature = "tracing")]
485 let _span = tracing::trace_span!("ParseStylesheet", servo_profiling = true).entered();
486 StyleStyleSheet::update_from_str(
487 &self.style_stylesheet(),
488 &text,
489 UrlExtraData(window.get_url().get_arc()),
490 None,
491 window.css_error_reporter(),
492 AllowImportRules::No, );
494
495 self.rulelist.set(None);
499
500 self.notify_invalidations();
502
503 Ok(())
504 }
505}