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 pub use_counters: UseCounters,
70
71 _forbid_construction: (),
74}
75
76impl StylesheetContents {
77 pub fn from_str(
80 css: &str,
81 url_data: UrlExtraData,
82 origin: Origin,
83 shared_lock: &SharedRwLock,
84 stylesheet_loader: Option<&dyn StylesheetLoader>,
85 error_reporter: Option<&dyn ParseErrorReporter>,
86 quirks_mode: QuirksMode,
87 allow_import_rules: AllowImportRules,
88 sanitization_data: Option<&mut SanitizationData>,
89 ) -> Arc<Self> {
90 let use_counters = UseCounters::default();
91 let (namespaces, rules, source_map_url, source_url) = Stylesheet::parse_rules(
92 css,
93 &url_data,
94 origin,
95 &shared_lock,
96 stylesheet_loader,
97 error_reporter,
98 quirks_mode,
99 Some(&use_counters),
100 allow_import_rules,
101 sanitization_data,
102 );
103
104 Arc::new(Self {
105 rules: CssRules::new(rules, &shared_lock),
106 origin,
107 url_data: RwLock::new(url_data),
108 namespaces: RwLock::new(namespaces),
109 quirks_mode,
110 source_map_url: RwLock::new(source_map_url),
111 source_url: RwLock::new(source_url),
112 use_counters,
113 _forbid_construction: (),
114 })
115 }
116
117 pub fn from_data(
129 rules: Arc<Locked<CssRules>>,
130 origin: Origin,
131 url_data: UrlExtraData,
132 quirks_mode: QuirksMode,
133 ) -> Arc<Self> {
134 Arc::new(Self {
135 rules,
136 origin,
137 url_data: RwLock::new(url_data),
138 namespaces: RwLock::new(Namespaces::default()),
139 quirks_mode,
140 source_map_url: RwLock::new(None),
141 source_url: RwLock::new(None),
142 use_counters: UseCounters::default(),
143 _forbid_construction: (),
144 })
145 }
146
147 pub fn from_shared_data(
149 rules: Arc<Locked<CssRules>>,
150 origin: Origin,
151 url_data: UrlExtraData,
152 quirks_mode: QuirksMode,
153 ) -> Arc<Self> {
154 debug_assert!(rules.is_static());
155 Self::from_data(rules, origin, url_data, quirks_mode)
156 }
157
158 #[inline]
160 pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
161 &self.rules.read_with(guard).0
162 }
163
164 #[cfg(feature = "gecko")]
166 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
167 if self.rules.is_static() {
168 return 0;
169 }
170 self.rules.unconditional_shallow_size_of(ops)
172 + self.rules.read_with(guard).size_of(guard, ops)
173 }
174}
175
176impl DeepCloneWithLock for StylesheetContents {
177 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> 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 use_counters: self.use_counters.clone(),
193 _forbid_construction: (),
194 }
195 }
196}
197
198#[derive(Debug)]
200pub struct Stylesheet {
201 pub contents: Arc<StylesheetContents>,
203 pub shared_lock: SharedRwLock,
205 pub media: Arc<Locked<MediaList>>,
207 pub disabled: AtomicBool,
209}
210
211pub trait StylesheetInDocument: ::std::fmt::Debug {
213 fn enabled(&self) -> bool;
215
216 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
218
219 fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
221 self.contents().rules(guard)
222 }
223
224 fn contents(&self) -> &StylesheetContents;
226
227 #[inline]
229 fn iter_rules<'a, 'b, C>(
230 &'a self,
231 device: &'a Device,
232 guard: &'a SharedRwLockReadGuard<'b>,
233 ) -> RulesIterator<'a, 'b, C>
234 where
235 C: NestedRuleIterationCondition,
236 {
237 let contents = self.contents();
238 RulesIterator::new(
239 device,
240 contents.quirks_mode,
241 guard,
242 contents.rules(guard).iter(),
243 )
244 }
245
246 fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
248 match self.media(guard) {
249 Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode),
250 None => true,
251 }
252 }
253
254 #[inline]
257 fn effective_rules<'a, 'b>(
258 &'a self,
259 device: &'a Device,
260 guard: &'a SharedRwLockReadGuard<'b>,
261 ) -> EffectiveRulesIterator<'a, 'b> {
262 self.iter_rules::<EffectiveRules>(device, guard)
263 }
264
265 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot>;
267}
268
269impl StylesheetInDocument for Stylesheet {
270 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
271 Some(self.media.read_with(guard))
272 }
273
274 fn enabled(&self) -> bool {
275 !self.disabled()
276 }
277
278 #[inline]
279 fn contents(&self) -> &StylesheetContents {
280 &self.contents
281 }
282
283 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
284 None
285 }
286}
287
288#[derive(Clone, Debug)]
291#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
292pub struct DocumentStyleSheet(
293 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
294);
295
296impl PartialEq for DocumentStyleSheet {
297 fn eq(&self, other: &Self) -> bool {
298 Arc::ptr_eq(&self.0, &other.0)
299 }
300}
301
302impl StylesheetInDocument for DocumentStyleSheet {
303 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
304 self.0.media(guard)
305 }
306
307 fn enabled(&self) -> bool {
308 self.0.enabled()
309 }
310
311 #[inline]
312 fn contents(&self) -> &StylesheetContents {
313 self.0.contents()
314 }
315
316 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
317 None
318 }
319}
320
321#[repr(u8)]
323#[derive(Clone, Copy, Debug, PartialEq)]
324pub enum SanitizationKind {
325 None,
327 Standard,
329 NoConditionalRules,
331}
332
333#[repr(u8)]
335#[derive(Clone, Copy, Debug, PartialEq)]
336pub enum AllowImportRules {
337 Yes,
339 No,
341}
342
343impl SanitizationKind {
344 fn allows(self, rule: &CssRule) -> bool {
345 debug_assert_ne!(self, SanitizationKind::None);
346 let is_standard = matches!(self, SanitizationKind::Standard);
350 match *rule {
351 CssRule::Document(..) |
352 CssRule::Media(..) |
353 CssRule::Supports(..) |
354 CssRule::Import(..) |
355 CssRule::Container(..) |
356 CssRule::LayerStatement(..) |
359 CssRule::LayerBlock(..) |
360 CssRule::Scope(..) |
363 CssRule::StartingStyle(..) => false,
364
365 CssRule::FontFace(..) |
366 CssRule::Namespace(..) |
367 CssRule::Style(..) |
368 CssRule::NestedDeclarations(..) |
369 CssRule::PositionTry(..) => true,
370
371 CssRule::Keyframes(..) |
372 CssRule::Page(..) |
373 CssRule::Margin(..) |
374 CssRule::Property(..) |
375 CssRule::FontFeatureValues(..) |
376 CssRule::FontPaletteValues(..) |
377 CssRule::CounterStyle(..) => !is_standard,
378 }
379 }
380}
381
382#[derive(Debug)]
384pub struct SanitizationData {
385 kind: SanitizationKind,
386 output: String,
387}
388
389impl SanitizationData {
390 #[inline]
392 pub fn new(kind: SanitizationKind) -> Option<Self> {
393 if matches!(kind, SanitizationKind::None) {
394 return None;
395 }
396 Some(Self {
397 kind,
398 output: String::new(),
399 })
400 }
401
402 #[inline]
404 pub fn take(self) -> String {
405 self.output
406 }
407}
408
409impl Stylesheet {
410 pub fn update_from_str(
412 existing: &Stylesheet,
413 css: &str,
414 url_data: UrlExtraData,
415 stylesheet_loader: Option<&dyn StylesheetLoader>,
416 error_reporter: Option<&dyn ParseErrorReporter>,
417 allow_import_rules: AllowImportRules,
418 ) {
419 let use_counters = UseCounters::default();
420 let (namespaces, rules, source_map_url, source_url) = Self::parse_rules(
421 css,
422 &url_data,
423 existing.contents.origin,
424 &existing.shared_lock,
425 stylesheet_loader,
426 error_reporter,
427 existing.contents.quirks_mode,
428 Some(&use_counters),
429 allow_import_rules,
430 None,
431 );
432
433 *existing.contents.url_data.write() = url_data;
434 *existing.contents.namespaces.write() = namespaces;
435
436 let mut guard = existing.shared_lock.write();
438 *existing.contents.rules.write_with(&mut guard) = CssRules(rules);
439 *existing.contents.source_map_url.write() = source_map_url;
440 *existing.contents.source_url.write() = source_url;
441 existing.contents.use_counters.merge(&use_counters);
442 }
443
444 fn parse_rules(
445 css: &str,
446 url_data: &UrlExtraData,
447 origin: Origin,
448 shared_lock: &SharedRwLock,
449 stylesheet_loader: Option<&dyn StylesheetLoader>,
450 error_reporter: Option<&dyn ParseErrorReporter>,
451 quirks_mode: QuirksMode,
452 use_counters: Option<&UseCounters>,
453 allow_import_rules: AllowImportRules,
454 mut sanitization_data: Option<&mut SanitizationData>,
455 ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
456 let mut input = ParserInput::new(css);
457 let mut input = Parser::new(&mut input);
458
459 let context = ParserContext::new(
460 origin,
461 url_data,
462 None,
463 ParsingMode::DEFAULT,
464 quirks_mode,
465 Default::default(),
466 error_reporter,
467 use_counters,
468 );
469
470 let mut rule_parser = TopLevelRuleParser {
471 shared_lock,
472 loader: stylesheet_loader,
473 context,
474 state: State::Start,
475 dom_error: None,
476 insert_rule_context: None,
477 allow_import_rules,
478 declaration_parser_state: Default::default(),
479 first_declaration_block: Default::default(),
480 wants_first_declaration_block: false,
481 error_reporting_state: Default::default(),
482 rules: Vec::new(),
483 };
484
485 {
486 let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
487 while let Some(result) = iter.next() {
488 match result {
489 Ok(rule_start) => {
490 if let Some(ref mut data) = sanitization_data {
492 if let Some(ref rule) = iter.parser.rules.last() {
493 if !data.kind.allows(rule) {
494 iter.parser.rules.pop();
495 continue;
496 }
497 }
498 let end = iter.input.position().byte_index();
499 data.output.push_str(&css[rule_start.byte_index()..end]);
500 }
501 },
502 Err((error, slice)) => {
503 let location = error.location;
504 let error = ContextualParseError::InvalidRule(slice, error);
505 iter.parser.context.log_css_error(location, error);
506 },
507 }
508 }
509 }
510
511 let source_map_url = input.current_source_map_url().map(String::from);
512 let source_url = input.current_source_url().map(String::from);
513 (
514 rule_parser.context.namespaces.into_owned(),
515 rule_parser.rules,
516 source_map_url,
517 source_url,
518 )
519 }
520
521 pub fn from_str(
527 css: &str,
528 url_data: UrlExtraData,
529 origin: Origin,
530 media: Arc<Locked<MediaList>>,
531 shared_lock: SharedRwLock,
532 stylesheet_loader: Option<&dyn StylesheetLoader>,
533 error_reporter: Option<&dyn ParseErrorReporter>,
534 quirks_mode: QuirksMode,
535 allow_import_rules: AllowImportRules,
536 ) -> Self {
537 let contents = StylesheetContents::from_str(
539 css,
540 url_data,
541 origin,
542 &shared_lock,
543 stylesheet_loader,
544 error_reporter,
545 quirks_mode,
546 allow_import_rules,
547 None,
548 );
549
550 Stylesheet {
551 contents,
552 shared_lock,
553 media,
554 disabled: AtomicBool::new(false),
555 }
556 }
557
558 pub fn disabled(&self) -> bool {
561 self.disabled.load(Ordering::SeqCst)
562 }
563
564 pub fn set_disabled(&self, disabled: bool) -> bool {
572 self.disabled.swap(disabled, Ordering::SeqCst) != disabled
573 }
574}
575
576#[cfg(feature = "servo")]
577impl Clone for Stylesheet {
578 fn clone(&self) -> Self {
579 let lock = self.shared_lock.clone();
581 let guard = self.shared_lock.read();
582
583 let media = self.media.read_with(&guard).clone();
585 let media = Arc::new(lock.wrap(media));
586 let contents = Arc::new(self.contents.deep_clone_with_lock(&lock, &guard));
587
588 Stylesheet {
589 contents,
590 media,
591 shared_lock: lock,
592 disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
593 }
594 }
595}