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(
178 &self,
179 lock: &SharedRwLock,
180 guard: &SharedRwLockReadGuard,
181 ) -> Self {
182 let rules = self
184 .rules
185 .read_with(guard)
186 .deep_clone_with_lock(lock, guard);
187
188 Self {
189 rules: Arc::new(lock.wrap(rules)),
190 quirks_mode: self.quirks_mode,
191 origin: self.origin,
192 url_data: RwLock::new((*self.url_data.read()).clone()),
193 namespaces: RwLock::new((*self.namespaces.read()).clone()),
194 source_map_url: RwLock::new((*self.source_map_url.read()).clone()),
195 source_url: RwLock::new((*self.source_url.read()).clone()),
196 use_counters: self.use_counters.clone(),
197 _forbid_construction: (),
198 }
199 }
200}
201
202#[derive(Debug)]
204pub struct Stylesheet {
205 pub contents: Arc<StylesheetContents>,
207 pub shared_lock: SharedRwLock,
209 pub media: Arc<Locked<MediaList>>,
211 pub disabled: AtomicBool,
213}
214
215pub trait StylesheetInDocument: ::std::fmt::Debug {
217 fn enabled(&self) -> bool;
219
220 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
222
223 fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
225 self.contents().rules(guard)
226 }
227
228 fn contents(&self) -> &StylesheetContents;
230
231 #[inline]
233 fn iter_rules<'a, 'b, C>(
234 &'a self,
235 device: &'a Device,
236 guard: &'a SharedRwLockReadGuard<'b>,
237 ) -> RulesIterator<'a, 'b, C>
238 where
239 C: NestedRuleIterationCondition,
240 {
241 let contents = self.contents();
242 RulesIterator::new(
243 device,
244 contents.quirks_mode,
245 guard,
246 contents.rules(guard).iter(),
247 )
248 }
249
250 fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
252 match self.media(guard) {
253 Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode),
254 None => true,
255 }
256 }
257
258 #[inline]
261 fn effective_rules<'a, 'b>(
262 &'a self,
263 device: &'a Device,
264 guard: &'a SharedRwLockReadGuard<'b>,
265 ) -> EffectiveRulesIterator<'a, 'b> {
266 self.iter_rules::<EffectiveRules>(device, guard)
267 }
268
269 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot>;
271}
272
273impl StylesheetInDocument for Stylesheet {
274 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
275 Some(self.media.read_with(guard))
276 }
277
278 fn enabled(&self) -> bool {
279 !self.disabled()
280 }
281
282 #[inline]
283 fn contents(&self) -> &StylesheetContents {
284 &self.contents
285 }
286
287 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
288 None
289 }
290}
291
292#[derive(Clone, Debug)]
295#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
296pub struct DocumentStyleSheet(
297 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
298);
299
300impl PartialEq for DocumentStyleSheet {
301 fn eq(&self, other: &Self) -> bool {
302 Arc::ptr_eq(&self.0, &other.0)
303 }
304}
305
306impl StylesheetInDocument for DocumentStyleSheet {
307 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
308 self.0.media(guard)
309 }
310
311 fn enabled(&self) -> bool {
312 self.0.enabled()
313 }
314
315 #[inline]
316 fn contents(&self) -> &StylesheetContents {
317 self.0.contents()
318 }
319
320 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
321 None
322 }
323}
324
325#[repr(u8)]
327#[derive(Clone, Copy, Debug, PartialEq)]
328pub enum SanitizationKind {
329 None,
331 Standard,
333 NoConditionalRules,
335}
336
337#[repr(u8)]
339#[derive(Clone, Copy, Debug, PartialEq)]
340pub enum AllowImportRules {
341 Yes,
343 No,
345}
346
347impl SanitizationKind {
348 fn allows(self, rule: &CssRule) -> bool {
349 debug_assert_ne!(self, SanitizationKind::None);
350 let is_standard = matches!(self, SanitizationKind::Standard);
354 match *rule {
355 CssRule::Document(..) |
356 CssRule::Media(..) |
357 CssRule::Supports(..) |
358 CssRule::Import(..) |
359 CssRule::Container(..) |
360 CssRule::LayerStatement(..) |
363 CssRule::LayerBlock(..) |
364 CssRule::Scope(..) |
367 CssRule::StartingStyle(..) => false,
368
369 CssRule::FontFace(..) |
370 CssRule::Namespace(..) |
371 CssRule::Style(..) |
372 CssRule::NestedDeclarations(..) |
373 CssRule::PositionTry(..) => true,
374
375 CssRule::Keyframes(..) |
376 CssRule::Page(..) |
377 CssRule::Margin(..) |
378 CssRule::Property(..) |
379 CssRule::FontFeatureValues(..) |
380 CssRule::FontPaletteValues(..) |
381 CssRule::CounterStyle(..) => !is_standard,
382 }
383 }
384}
385
386#[derive(Debug)]
388pub struct SanitizationData {
389 kind: SanitizationKind,
390 output: String,
391}
392
393impl SanitizationData {
394 #[inline]
396 pub fn new(kind: SanitizationKind) -> Option<Self> {
397 if matches!(kind, SanitizationKind::None) {
398 return None;
399 }
400 Some(Self {
401 kind,
402 output: String::new(),
403 })
404 }
405
406 #[inline]
408 pub fn take(self) -> String {
409 self.output
410 }
411}
412
413impl Stylesheet {
414 pub fn update_from_str(
416 existing: &Stylesheet,
417 css: &str,
418 url_data: UrlExtraData,
419 stylesheet_loader: Option<&dyn StylesheetLoader>,
420 error_reporter: Option<&dyn ParseErrorReporter>,
421 allow_import_rules: AllowImportRules,
422 ) {
423 let use_counters = UseCounters::default();
424 let (namespaces, rules, source_map_url, source_url) = Self::parse_rules(
425 css,
426 &url_data,
427 existing.contents.origin,
428 &existing.shared_lock,
429 stylesheet_loader,
430 error_reporter,
431 existing.contents.quirks_mode,
432 Some(&use_counters),
433 allow_import_rules,
434 None,
435 );
436
437 *existing.contents.url_data.write() = url_data;
438 *existing.contents.namespaces.write() = namespaces;
439
440 let mut guard = existing.shared_lock.write();
442 *existing.contents.rules.write_with(&mut guard) = CssRules(rules);
443 *existing.contents.source_map_url.write() = source_map_url;
444 *existing.contents.source_url.write() = source_url;
445 existing.contents.use_counters.merge(&use_counters);
446 }
447
448 fn parse_rules(
449 css: &str,
450 url_data: &UrlExtraData,
451 origin: Origin,
452 shared_lock: &SharedRwLock,
453 stylesheet_loader: Option<&dyn StylesheetLoader>,
454 error_reporter: Option<&dyn ParseErrorReporter>,
455 quirks_mode: QuirksMode,
456 use_counters: Option<&UseCounters>,
457 allow_import_rules: AllowImportRules,
458 mut sanitization_data: Option<&mut SanitizationData>,
459 ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
460 let mut input = ParserInput::new(css);
461 let mut input = Parser::new(&mut input);
462
463 let context = ParserContext::new(
464 origin,
465 url_data,
466 None,
467 ParsingMode::DEFAULT,
468 quirks_mode,
469 Default::default(),
470 error_reporter,
471 use_counters,
472 );
473
474 let mut rule_parser = TopLevelRuleParser {
475 shared_lock,
476 loader: stylesheet_loader,
477 context,
478 state: State::Start,
479 dom_error: None,
480 insert_rule_context: None,
481 allow_import_rules,
482 declaration_parser_state: Default::default(),
483 first_declaration_block: Default::default(),
484 wants_first_declaration_block: false,
485 error_reporting_state: Default::default(),
486 rules: Vec::new(),
487 };
488
489 {
490 let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
491 while let Some(result) = iter.next() {
492 match result {
493 Ok(rule_start) => {
494 if let Some(ref mut data) = sanitization_data {
496 if let Some(ref rule) = iter.parser.rules.last() {
497 if !data.kind.allows(rule) {
498 iter.parser.rules.pop();
499 continue;
500 }
501 }
502 let end = iter.input.position().byte_index();
503 data.output.push_str(&css[rule_start.byte_index()..end]);
504 }
505 },
506 Err((error, slice)) => {
507 let location = error.location;
508 let error = ContextualParseError::InvalidRule(slice, error);
509 iter.parser.context.log_css_error(location, error);
510 },
511 }
512 }
513 }
514
515 let source_map_url = input.current_source_map_url().map(String::from);
516 let source_url = input.current_source_url().map(String::from);
517 (
518 rule_parser.context.namespaces.into_owned(),
519 rule_parser.rules,
520 source_map_url,
521 source_url,
522 )
523 }
524
525 pub fn from_str(
531 css: &str,
532 url_data: UrlExtraData,
533 origin: Origin,
534 media: Arc<Locked<MediaList>>,
535 shared_lock: SharedRwLock,
536 stylesheet_loader: Option<&dyn StylesheetLoader>,
537 error_reporter: Option<&dyn ParseErrorReporter>,
538 quirks_mode: QuirksMode,
539 allow_import_rules: AllowImportRules,
540 ) -> Self {
541 let contents = StylesheetContents::from_str(
543 css,
544 url_data,
545 origin,
546 &shared_lock,
547 stylesheet_loader,
548 error_reporter,
549 quirks_mode,
550 allow_import_rules,
551 None,
552 );
553
554 Stylesheet {
555 contents,
556 shared_lock,
557 media,
558 disabled: AtomicBool::new(false),
559 }
560 }
561
562 pub fn disabled(&self) -> bool {
565 self.disabled.load(Ordering::SeqCst)
566 }
567
568 pub fn set_disabled(&self, disabled: bool) -> bool {
576 self.disabled.swap(disabled, Ordering::SeqCst) != disabled
577 }
578}
579
580#[cfg(feature = "servo")]
581impl Clone for Stylesheet {
582 fn clone(&self) -> Self {
583 let lock = self.shared_lock.clone();
585 let guard = self.shared_lock.read();
586
587 let media = self.media.read_with(&guard).clone();
589 let media = Arc::new(lock.wrap(media));
590 let contents = Arc::new(self.contents.deep_clone_with_lock(&lock, &guard));
591
592 Stylesheet {
593 contents,
594 media,
595 shared_lock: lock,
596 disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
597 }
598 }
599}