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 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
107 #[allow(clippy::too_many_arguments)]
108 pub(crate) fn new(
109 window: &Window,
110 owner: Option<&Element>,
111 type_: DOMString,
112 href: Option<DOMString>,
113 title: Option<DOMString>,
114 stylesheet: Arc<StyleStyleSheet>,
115 constructor_document: Option<&Document>,
116 can_gc: CanGc,
117 ) -> DomRoot<CSSStyleSheet> {
118 reflect_dom_object(
119 Box::new(CSSStyleSheet::new_inherited(
120 owner,
121 type_,
122 href,
123 title,
124 stylesheet,
125 constructor_document,
126 )),
127 window,
128 can_gc,
129 )
130 }
131
132 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
133 #[allow(clippy::too_many_arguments)]
134 fn new_with_proto(
135 window: &Window,
136 proto: Option<HandleObject>,
137 owner: Option<&Element>,
138 type_: DOMString,
139 href: Option<DOMString>,
140 title: Option<DOMString>,
141 stylesheet: Arc<StyleStyleSheet>,
142 constructor_document: Option<&Document>,
143 can_gc: CanGc,
144 ) -> DomRoot<CSSStyleSheet> {
145 reflect_dom_object_with_proto(
146 Box::new(CSSStyleSheet::new_inherited(
147 owner,
148 type_,
149 href,
150 title,
151 stylesheet,
152 constructor_document,
153 )),
154 window,
155 proto,
156 can_gc,
157 )
158 }
159
160 fn rulelist(&self, can_gc: CanGc) -> DomRoot<CSSRuleList> {
161 self.rulelist.or_init(|| {
162 let sheet = self.style_stylesheet.borrow();
163 let guard = sheet.shared_lock.read();
164 let rules = sheet.contents(&guard).rules.clone();
165 CSSRuleList::new(
166 self.global().as_window(),
167 self,
168 RulesSource::Rules(rules),
169 can_gc,
170 )
171 })
172 }
173
174 pub(crate) fn disabled(&self) -> bool {
175 self.style_stylesheet.borrow().disabled()
176 }
177
178 pub(crate) fn owner_node(&self) -> Option<DomRoot<Element>> {
179 self.owner_node.get()
180 }
181
182 pub(crate) fn set_disabled(&self, disabled: bool) {
183 if self.style_stylesheet.borrow().set_disabled(disabled) {
184 self.notify_invalidations();
185 }
186 }
187
188 pub(crate) fn set_owner_node(&self, value: Option<&Element>) {
189 self.owner_node.set(value);
190 }
191
192 pub(crate) fn shared_lock(&self) -> &SharedRwLock {
193 &self.style_shared_lock
194 }
195
196 pub(crate) fn style_stylesheet(&self) -> Ref<'_, Arc<StyleStyleSheet>> {
197 self.style_stylesheet.borrow()
198 }
199
200 pub(crate) fn set_origin_clean(&self, origin_clean: bool) {
201 self.origin_clean.set(origin_clean);
202 }
203
204 pub(crate) fn medialist(&self, can_gc: CanGc) -> DomRoot<MediaList> {
205 MediaList::new(
206 self.global().as_window(),
207 self,
208 self.style_stylesheet().media.clone(),
209 can_gc,
210 )
211 }
212
213 #[inline]
215 pub(crate) fn is_constructed(&self) -> bool {
216 self.constructor_document.is_some()
217 }
218
219 pub(crate) fn constructor_document_matches(&self, other_doc: &Document) -> bool {
220 match &self.constructor_document {
221 Some(doc) => *doc == other_doc,
222 None => false,
223 }
224 }
225
226 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
229 pub(crate) fn add_adopter(&self, owner: StyleSheetListOwner) {
230 debug_assert!(self.is_constructed());
231 self.adopters.borrow_mut().push(owner);
232 }
233
234 pub(crate) fn remove_adopter(&self, owner: &StyleSheetListOwner) {
235 let adopters = &mut *self.adopters.borrow_mut();
236 if let Some(index) = adopters.iter().position(|o| o == owner) {
237 adopters.swap_remove(index);
238 }
239 }
240
241 pub(crate) fn will_modify(&self) {
242 let Some(node) = self.owner_node.get() else {
243 return;
244 };
245
246 let Some(node) = node.downcast::<HTMLStyleElement>() else {
247 return;
248 };
249
250 node.will_modify_stylesheet();
251 }
252
253 pub(crate) fn update_style_stylesheet(
254 &self,
255 style_stylesheet: &Arc<StyleStyleSheet>,
256 guard: &SharedRwLockReadGuard,
257 ) {
258 *self.style_stylesheet.borrow_mut() = style_stylesheet.clone();
265 if let Some(rulelist) = self.rulelist.get() {
266 let rules = style_stylesheet.contents(guard).rules.clone();
267 rulelist.update_rules(RulesSource::Rules(rules), guard);
268 }
269 }
270
271 pub(crate) fn notify_invalidations(&self) {
273 if let Some(owner) = self.owner_node() {
274 owner.stylesheet_list_owner().invalidate_stylesheets();
275 }
276 for adopter in self.adopters.borrow().iter() {
277 adopter.invalidate_stylesheets();
278 }
279 }
280
281 pub(crate) fn disallow_modification(&self) -> bool {
283 self.disallow_modification.get()
284 }
285
286 fn do_replace_sync(&self, text: USVString) {
288 let global = self.global();
290 let window = global.as_window();
291
292 self.will_modify();
293
294 #[cfg(feature = "tracing")]
295 let _span = tracing::trace_span!("ParseStylesheet", servo_profiling = true).entered();
296 let sheet = self.style_stylesheet();
297 let new_contents = StylesheetContents::from_str(
298 &text,
299 UrlExtraData(window.get_url().get_arc()),
300 Origin::Author,
301 &self.style_shared_lock,
302 None,
303 window.css_error_reporter(),
304 window.Document().quirks_mode(),
305 AllowImportRules::No, None,
307 );
308
309 {
310 let mut write_guard = self.style_shared_lock.write();
311 *sheet.contents.write_with(&mut write_guard) = new_contents;
312 }
313
314 self.rulelist.set(None);
318
319 self.notify_invalidations();
321 }
322}
323
324impl CSSStyleSheetMethods<crate::DomTypeHolder> for CSSStyleSheet {
325 fn Constructor(
327 window: &Window,
328 proto: Option<HandleObject>,
329 can_gc: CanGc,
330 options: &CSSStyleSheetInit,
331 ) -> DomRoot<Self> {
332 let doc = window.Document();
333 let shared_lock = doc.style_shared_lock().clone();
334 let media = Arc::new(shared_lock.wrap(match &options.media {
335 Some(media) => match media {
336 MediaListOrString::MediaList(media_list) => media_list.clone_media_list(),
337 MediaListOrString::String(str) => MediaList::parse_media_list(&str.str(), window),
338 },
339 None => StyleMediaList::empty(),
340 }));
341 let stylesheet = Arc::new(StyleStyleSheet::from_str(
342 "",
343 UrlExtraData(window.get_url().get_arc()),
344 Origin::Author,
345 media,
346 shared_lock,
347 None,
348 window.css_error_reporter(),
349 doc.quirks_mode(),
350 AllowImportRules::No,
351 ));
352 if options.disabled {
353 stylesheet.set_disabled(true);
354 }
355 Self::new_with_proto(
356 window,
357 proto,
358 None, "text/css".into(),
360 None, None, stylesheet,
363 Some(&window.Document()), can_gc,
365 )
366 }
367
368 fn GetCssRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> {
370 if !self.origin_clean.get() {
371 return Err(Error::Security);
372 }
373 Ok(self.rulelist(can_gc))
374 }
375
376 fn InsertRule(&self, rule: DOMString, index: u32, can_gc: CanGc) -> Fallible<u32> {
378 if !self.origin_clean.get() {
380 return Err(Error::Security);
381 }
382
383 if self.disallow_modification() {
385 return Err(Error::NotAllowed);
386 }
387
388 self.rulelist(can_gc)
389 .insert_rule(&rule, index, CssRuleTypes::default(), None, can_gc)
390 }
391
392 fn DeleteRule(&self, index: u32, can_gc: CanGc) -> ErrorResult {
394 if !self.origin_clean.get() {
396 return Err(Error::Security);
397 }
398
399 if self.disallow_modification() {
401 return Err(Error::NotAllowed);
402 }
403 self.rulelist(can_gc).remove_rule(index)
404 }
405
406 fn GetRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> {
408 self.GetCssRules(can_gc)
409 }
410
411 fn RemoveRule(&self, index: u32, can_gc: CanGc) -> ErrorResult {
413 self.DeleteRule(index, can_gc)
414 }
415
416 fn AddRule(
418 &self,
419 selector: DOMString,
420 block: DOMString,
421 optional_index: Option<u32>,
422 can_gc: CanGc,
423 ) -> Fallible<i32> {
424 let mut rule = selector;
427
428 if block.is_empty() {
432 rule.push_str(" { }");
433 } else {
434 rule.push_str(" { ");
435 rule.push_str(&block.str());
436 rule.push_str(" }");
437 };
438
439 let index = optional_index.unwrap_or_else(|| self.rulelist(can_gc).Length());
441
442 self.InsertRule(rule, index, can_gc)?;
444
445 Ok(-1)
447 }
448
449 fn Replace(&self, text: USVString, comp: InRealm, can_gc: CanGc) -> Fallible<Rc<Promise>> {
451 let promise = Promise::new_in_current_realm(comp, can_gc);
453
454 if !self.is_constructed() || self.disallow_modification() {
457 return Err(Error::NotAllowed);
458 }
459
460 self.disallow_modification.set(true);
462
463 let trusted_sheet = Trusted::new(self);
465 let trusted_promise = TrustedPromise::new(promise.clone());
466
467 self.global()
468 .task_manager()
469 .dom_manipulation_task_source()
470 .queue(task!(cssstylesheet_replace: move || {
471 let sheet = trusted_sheet.root();
472
473 sheet.do_replace_sync(text);
475
476 sheet.disallow_modification.set(false);
478
479 trusted_promise.root().resolve_native(&sheet, CanGc::note());
481 }));
482
483 Ok(promise)
484 }
485
486 fn ReplaceSync(&self, text: USVString) -> Result<(), Error> {
488 if !self.is_constructed() || self.disallow_modification() {
491 return Err(Error::NotAllowed);
492 }
493 self.do_replace_sync(text);
494 Ok(())
495 }
496}