1use crate::context::QuirksMode;
6use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
7use crate::media_queries::{Device, MediaList};
8use crate::parser::ParserContext;
9use crate::shared_lock::{DeepCloneWithLock, Locked};
10use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
11use crate::stylesheets::loader::StylesheetLoader;
12use crate::stylesheets::rule_parser::{State, TopLevelRuleParser};
13use crate::stylesheets::rules_iterator::{EffectiveRules, EffectiveRulesIterator};
14use crate::stylesheets::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
15use crate::stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
16use crate::use_counters::UseCounters;
17use crate::{Namespace, Prefix};
18use cssparser::{Parser, ParserInput, StyleSheetParser};
19use fxhash::FxHashMap;
20#[cfg(feature = "gecko")]
21use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
22use parking_lot::RwLock;
23use servo_arc::Arc;
24use std::sync::atomic::{AtomicBool, Ordering};
25use style_traits::ParsingMode;
26
27use super::scope_rule::ImplicitScopeRoot;
28
29pub struct UserAgentStylesheets {
31 pub shared_lock: SharedRwLock,
33 pub user_or_user_agent_stylesheets: Vec<DocumentStyleSheet>,
35 pub quirks_mode_stylesheet: DocumentStyleSheet,
37}
38
39#[derive(Clone, Debug, Default, MallocSizeOf)]
43#[allow(missing_docs)]
44pub struct Namespaces {
45 pub default: Option<Namespace>,
46 pub prefixes: FxHashMap<Prefix, Namespace>,
47}
48
49#[derive(Debug)]
52pub struct StylesheetContents {
53 pub rules: Arc<Locked<CssRules>>,
56 pub origin: Origin,
58 pub url_data: RwLock<UrlExtraData>,
60 pub namespaces: RwLock<Namespaces>,
62 pub quirks_mode: QuirksMode,
64 pub source_map_url: RwLock<Option<String>>,
66 pub source_url: RwLock<Option<String>>,
68
69 _forbid_construction: (),
72}
73
74impl StylesheetContents {
75 pub fn from_str(
78 css: &str,
79 url_data: UrlExtraData,
80 origin: Origin,
81 shared_lock: &SharedRwLock,
82 stylesheet_loader: Option<&dyn StylesheetLoader>,
83 error_reporter: Option<&dyn ParseErrorReporter>,
84 quirks_mode: QuirksMode,
85 use_counters: Option<&UseCounters>,
86 allow_import_rules: AllowImportRules,
87 sanitization_data: Option<&mut SanitizationData>,
88 ) -> Arc<Self> {
89 let (namespaces, rules, source_map_url, source_url) = Stylesheet::parse_rules(
90 css,
91 &url_data,
92 origin,
93 &shared_lock,
94 stylesheet_loader,
95 error_reporter,
96 quirks_mode,
97 use_counters,
98 allow_import_rules,
99 sanitization_data,
100 );
101
102 Arc::new(Self {
103 rules: CssRules::new(rules, &shared_lock),
104 origin,
105 url_data: RwLock::new(url_data),
106 namespaces: RwLock::new(namespaces),
107 quirks_mode,
108 source_map_url: RwLock::new(source_map_url),
109 source_url: RwLock::new(source_url),
110 _forbid_construction: (),
111 })
112 }
113
114 pub fn from_data(
126 rules: Arc<Locked<CssRules>>,
127 origin: Origin,
128 url_data: UrlExtraData,
129 quirks_mode: QuirksMode,
130 ) -> Arc<Self> {
131 Arc::new(Self {
132 rules,
133 origin,
134 url_data: RwLock::new(url_data),
135 namespaces: RwLock::new(Namespaces::default()),
136 quirks_mode,
137 source_map_url: RwLock::new(None),
138 source_url: RwLock::new(None),
139 _forbid_construction: (),
140 })
141 }
142
143 pub fn from_shared_data(
145 rules: Arc<Locked<CssRules>>,
146 origin: Origin,
147 url_data: UrlExtraData,
148 quirks_mode: QuirksMode,
149 ) -> Arc<Self> {
150 debug_assert!(rules.is_static());
151 Self::from_data(rules, origin, url_data, quirks_mode)
152 }
153
154 #[inline]
156 pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
157 &self.rules.read_with(guard).0
158 }
159
160 #[cfg(feature = "gecko")]
162 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
163 if self.rules.is_static() {
164 return 0;
165 }
166 self.rules.unconditional_shallow_size_of(ops) +
168 self.rules.read_with(guard).size_of(guard, ops)
169 }
170}
171
172impl DeepCloneWithLock for StylesheetContents {
173 fn deep_clone_with_lock(
174 &self,
175 lock: &SharedRwLock,
176 guard: &SharedRwLockReadGuard,
177 ) -> Self {
178 let rules = self
180 .rules
181 .read_with(guard)
182 .deep_clone_with_lock(lock, guard);
183
184 Self {
185 rules: Arc::new(lock.wrap(rules)),
186 quirks_mode: self.quirks_mode,
187 origin: self.origin,
188 url_data: RwLock::new((*self.url_data.read()).clone()),
189 namespaces: RwLock::new((*self.namespaces.read()).clone()),
190 source_map_url: RwLock::new((*self.source_map_url.read()).clone()),
191 source_url: RwLock::new((*self.source_url.read()).clone()),
192 _forbid_construction: (),
193 }
194 }
195}
196
197#[derive(Debug)]
199pub struct Stylesheet {
200 pub contents: Arc<StylesheetContents>,
202 pub shared_lock: SharedRwLock,
204 pub media: Arc<Locked<MediaList>>,
206 pub disabled: AtomicBool,
208}
209
210pub trait StylesheetInDocument: ::std::fmt::Debug {
212 fn enabled(&self) -> bool;
214
215 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
217
218 fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
220 self.contents().rules(guard)
221 }
222
223 fn contents(&self) -> &StylesheetContents;
225
226 #[inline]
228 fn iter_rules<'a, 'b, C>(
229 &'a self,
230 device: &'a Device,
231 guard: &'a SharedRwLockReadGuard<'b>,
232 ) -> RulesIterator<'a, 'b, C>
233 where
234 C: NestedRuleIterationCondition,
235 {
236 let contents = self.contents();
237 RulesIterator::new(
238 device,
239 contents.quirks_mode,
240 guard,
241 contents.rules(guard).iter(),
242 )
243 }
244
245 fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
247 match self.media(guard) {
248 Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode),
249 None => true,
250 }
251 }
252
253 #[inline]
256 fn effective_rules<'a, 'b>(
257 &'a self,
258 device: &'a Device,
259 guard: &'a SharedRwLockReadGuard<'b>,
260 ) -> EffectiveRulesIterator<'a, 'b> {
261 self.iter_rules::<EffectiveRules>(device, guard)
262 }
263
264 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot>;
266}
267
268impl StylesheetInDocument for Stylesheet {
269 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
270 Some(self.media.read_with(guard))
271 }
272
273 fn enabled(&self) -> bool {
274 !self.disabled()
275 }
276
277 #[inline]
278 fn contents(&self) -> &StylesheetContents {
279 &self.contents
280 }
281
282 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
283 None
284 }
285}
286
287#[derive(Clone, Debug)]
290#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
291pub struct DocumentStyleSheet(
292 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
293);
294
295impl PartialEq for DocumentStyleSheet {
296 fn eq(&self, other: &Self) -> bool {
297 Arc::ptr_eq(&self.0, &other.0)
298 }
299}
300
301impl StylesheetInDocument for DocumentStyleSheet {
302 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
303 self.0.media(guard)
304 }
305
306 fn enabled(&self) -> bool {
307 self.0.enabled()
308 }
309
310 #[inline]
311 fn contents(&self) -> &StylesheetContents {
312 self.0.contents()
313 }
314
315 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
316 None
317 }
318}
319
320#[repr(u8)]
322#[derive(Clone, Copy, Debug, PartialEq)]
323pub enum SanitizationKind {
324 None,
326 Standard,
328 NoConditionalRules,
330}
331
332#[repr(u8)]
334#[derive(Clone, Copy, Debug, PartialEq)]
335pub enum AllowImportRules {
336 Yes,
338 No,
340}
341
342impl SanitizationKind {
343 fn allows(self, rule: &CssRule) -> bool {
344 debug_assert_ne!(self, SanitizationKind::None);
345 let is_standard = matches!(self, SanitizationKind::Standard);
349 match *rule {
350 CssRule::Document(..) |
351 CssRule::Media(..) |
352 CssRule::Supports(..) |
353 CssRule::Import(..) |
354 CssRule::Container(..) |
355 CssRule::LayerStatement(..) |
358 CssRule::LayerBlock(..) |
359 CssRule::Scope(..) |
362 CssRule::StartingStyle(..) => false,
363
364 CssRule::FontFace(..) |
365 CssRule::Namespace(..) |
366 CssRule::Style(..) |
367 CssRule::NestedDeclarations(..) |
368 CssRule::PositionTry(..) => true,
369
370 CssRule::Keyframes(..) |
371 CssRule::Page(..) |
372 CssRule::Margin(..) |
373 CssRule::Property(..) |
374 CssRule::FontFeatureValues(..) |
375 CssRule::FontPaletteValues(..) |
376 CssRule::CounterStyle(..) => !is_standard,
377 }
378 }
379}
380
381#[derive(Debug)]
383pub struct SanitizationData {
384 kind: SanitizationKind,
385 output: String,
386}
387
388impl SanitizationData {
389 #[inline]
391 pub fn new(kind: SanitizationKind) -> Option<Self> {
392 if matches!(kind, SanitizationKind::None) {
393 return None;
394 }
395 Some(Self {
396 kind,
397 output: String::new(),
398 })
399 }
400
401 #[inline]
403 pub fn take(self) -> String {
404 self.output
405 }
406}
407
408impl Stylesheet {
409 pub fn update_from_str(
411 existing: &Stylesheet,
412 css: &str,
413 url_data: UrlExtraData,
414 stylesheet_loader: Option<&dyn StylesheetLoader>,
415 error_reporter: Option<&dyn ParseErrorReporter>,
416 allow_import_rules: AllowImportRules,
417 ) {
418 let (namespaces, rules, source_map_url, source_url) = Self::parse_rules(
420 css,
421 &url_data,
422 existing.contents.origin,
423 &existing.shared_lock,
424 stylesheet_loader,
425 error_reporter,
426 existing.contents.quirks_mode,
427 None,
428 allow_import_rules,
429 None,
430 );
431
432 *existing.contents.url_data.write() = url_data;
433 *existing.contents.namespaces.write() = namespaces;
434
435 let mut guard = existing.shared_lock.write();
437 *existing.contents.rules.write_with(&mut guard) = CssRules(rules);
438 *existing.contents.source_map_url.write() = source_map_url;
439 *existing.contents.source_url.write() = source_url;
440 }
441
442 fn parse_rules(
443 css: &str,
444 url_data: &UrlExtraData,
445 origin: Origin,
446 shared_lock: &SharedRwLock,
447 stylesheet_loader: Option<&dyn StylesheetLoader>,
448 error_reporter: Option<&dyn ParseErrorReporter>,
449 quirks_mode: QuirksMode,
450 use_counters: Option<&UseCounters>,
451 allow_import_rules: AllowImportRules,
452 mut sanitization_data: Option<&mut SanitizationData>,
453 ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
454 let mut input = ParserInput::new(css);
455 let mut input = Parser::new(&mut input);
456
457 let context = ParserContext::new(
458 origin,
459 url_data,
460 None,
461 ParsingMode::DEFAULT,
462 quirks_mode,
463 Default::default(),
464 error_reporter,
465 use_counters,
466 );
467
468 let mut rule_parser = TopLevelRuleParser {
469 shared_lock,
470 loader: stylesheet_loader,
471 context,
472 state: State::Start,
473 dom_error: None,
474 insert_rule_context: None,
475 allow_import_rules,
476 declaration_parser_state: Default::default(),
477 first_declaration_block: Default::default(),
478 wants_first_declaration_block: false,
479 error_reporting_state: Default::default(),
480 rules: Vec::new(),
481 };
482
483 {
484 let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
485 while let Some(result) = iter.next() {
486 match result {
487 Ok(rule_start) => {
488 if let Some(ref mut data) = sanitization_data {
490 if let Some(ref rule) = iter.parser.rules.last() {
491 if !data.kind.allows(rule) {
492 iter.parser.rules.pop();
493 continue;
494 }
495 }
496 let end = iter.input.position().byte_index();
497 data.output.push_str(&css[rule_start.byte_index()..end]);
498 }
499 },
500 Err((error, slice)) => {
501 let location = error.location;
502 let error = ContextualParseError::InvalidRule(slice, error);
503 iter.parser.context.log_css_error(location, error);
504 },
505 }
506 }
507 }
508
509 let source_map_url = input.current_source_map_url().map(String::from);
510 let source_url = input.current_source_url().map(String::from);
511 (
512 rule_parser.context.namespaces.into_owned(),
513 rule_parser.rules,
514 source_map_url,
515 source_url,
516 )
517 }
518
519 pub fn from_str(
525 css: &str,
526 url_data: UrlExtraData,
527 origin: Origin,
528 media: Arc<Locked<MediaList>>,
529 shared_lock: SharedRwLock,
530 stylesheet_loader: Option<&dyn StylesheetLoader>,
531 error_reporter: Option<&dyn ParseErrorReporter>,
532 quirks_mode: QuirksMode,
533 allow_import_rules: AllowImportRules,
534 ) -> Self {
535 let contents = StylesheetContents::from_str(
537 css,
538 url_data,
539 origin,
540 &shared_lock,
541 stylesheet_loader,
542 error_reporter,
543 quirks_mode,
544 None,
545 allow_import_rules,
546 None,
547 );
548
549 Stylesheet {
550 contents,
551 shared_lock,
552 media,
553 disabled: AtomicBool::new(false),
554 }
555 }
556
557 pub fn disabled(&self) -> bool {
560 self.disabled.load(Ordering::SeqCst)
561 }
562
563 pub fn set_disabled(&self, disabled: bool) -> bool {
571 self.disabled.swap(disabled, Ordering::SeqCst) != disabled
572 }
573}
574
575#[cfg(feature = "servo")]
576impl Clone for Stylesheet {
577 fn clone(&self) -> Self {
578 let lock = self.shared_lock.clone();
580 let guard = self.shared_lock.read();
581
582 let media = self.media.read_with(&guard).clone();
584 let media = Arc::new(lock.wrap(media));
585 let contents = Arc::new(self.contents.deep_clone_with_lock(&lock, &guard));
586
587 Stylesheet {
588 contents,
589 media,
590 shared_lock: lock,
591 disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
592 }
593 }
594}