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};
19#[cfg(feature = "gecko")]
20use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
21use parking_lot::RwLock;
22use rustc_hash::FxHashMap;
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_shared_data(
129 rules: Arc<Locked<CssRules>>,
130 origin: Origin,
131 url_data: UrlExtraData,
132 quirks_mode: QuirksMode,
133 ) -> Arc<Self> {
134 debug_assert!(rules.is_static());
135 Arc::new(Self {
136 rules,
137 origin,
138 url_data: RwLock::new(url_data),
139 namespaces: RwLock::new(Namespaces::default()),
140 quirks_mode,
141 source_map_url: RwLock::new(None),
142 source_url: RwLock::new(None),
143 use_counters: UseCounters::default(),
144 _forbid_construction: (),
145 })
146 }
147
148 #[inline]
150 pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
151 &self.rules.read_with(guard).0
152 }
153
154 #[cfg(feature = "gecko")]
156 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
157 if self.rules.is_static() {
158 return 0;
159 }
160 self.rules.unconditional_shallow_size_of(ops)
162 + self.rules.read_with(guard).size_of(guard, ops)
163 }
164}
165
166impl DeepCloneWithLock for StylesheetContents {
167 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
168 let rules = self
170 .rules
171 .read_with(guard)
172 .deep_clone_with_lock(lock, guard);
173
174 Self {
175 rules: Arc::new(lock.wrap(rules)),
176 quirks_mode: self.quirks_mode,
177 origin: self.origin,
178 url_data: RwLock::new((*self.url_data.read()).clone()),
179 namespaces: RwLock::new((*self.namespaces.read()).clone()),
180 source_map_url: RwLock::new((*self.source_map_url.read()).clone()),
181 source_url: RwLock::new((*self.source_url.read()).clone()),
182 use_counters: self.use_counters.clone(),
183 _forbid_construction: (),
184 }
185 }
186}
187
188#[derive(Debug)]
190pub struct Stylesheet {
191 pub contents: Arc<StylesheetContents>,
193 pub shared_lock: SharedRwLock,
195 pub media: Arc<Locked<MediaList>>,
197 pub disabled: AtomicBool,
199}
200
201pub trait StylesheetInDocument: ::std::fmt::Debug {
203 fn enabled(&self) -> bool;
205
206 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
208
209 fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
211 self.contents().rules(guard)
212 }
213
214 fn contents(&self) -> &StylesheetContents;
216
217 #[inline]
219 fn iter_rules<'a, 'b, C>(
220 &'a self,
221 device: &'a Device,
222 guard: &'a SharedRwLockReadGuard<'b>,
223 ) -> RulesIterator<'a, 'b, C>
224 where
225 C: NestedRuleIterationCondition,
226 {
227 let contents = self.contents();
228 RulesIterator::new(
229 device,
230 contents.quirks_mode,
231 guard,
232 contents.rules(guard).iter(),
233 )
234 }
235
236 fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
238 match self.media(guard) {
239 Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode),
240 None => true,
241 }
242 }
243
244 #[inline]
247 fn effective_rules<'a, 'b>(
248 &'a self,
249 device: &'a Device,
250 guard: &'a SharedRwLockReadGuard<'b>,
251 ) -> EffectiveRulesIterator<'a, 'b> {
252 self.iter_rules::<EffectiveRules>(device, guard)
253 }
254
255 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot>;
257}
258
259impl StylesheetInDocument for Stylesheet {
260 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
261 Some(self.media.read_with(guard))
262 }
263
264 fn enabled(&self) -> bool {
265 !self.disabled()
266 }
267
268 #[inline]
269 fn contents(&self) -> &StylesheetContents {
270 &self.contents
271 }
272
273 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
274 None
275 }
276}
277
278#[derive(Clone, Debug)]
281#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
282pub struct DocumentStyleSheet(
283 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
284);
285
286impl PartialEq for DocumentStyleSheet {
287 fn eq(&self, other: &Self) -> bool {
288 Arc::ptr_eq(&self.0, &other.0)
289 }
290}
291
292impl StylesheetInDocument for DocumentStyleSheet {
293 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
294 self.0.media(guard)
295 }
296
297 fn enabled(&self) -> bool {
298 self.0.enabled()
299 }
300
301 #[inline]
302 fn contents(&self) -> &StylesheetContents {
303 self.0.contents()
304 }
305
306 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
307 None
308 }
309}
310
311#[repr(u8)]
313#[derive(Clone, Copy, Debug, PartialEq)]
314pub enum SanitizationKind {
315 None,
317 Standard,
319 NoConditionalRules,
321}
322
323#[repr(u8)]
325#[derive(Clone, Copy, Debug, PartialEq)]
326pub enum AllowImportRules {
327 Yes,
329 No,
331}
332
333impl SanitizationKind {
334 fn allows(self, rule: &CssRule) -> bool {
335 debug_assert_ne!(self, SanitizationKind::None);
336 let is_standard = matches!(self, SanitizationKind::Standard);
340 match *rule {
341 CssRule::Document(..) |
342 CssRule::Media(..) |
343 CssRule::Supports(..) |
344 CssRule::Import(..) |
345 CssRule::Container(..) |
346 CssRule::LayerStatement(..) |
349 CssRule::LayerBlock(..) |
350 CssRule::Scope(..) |
353 CssRule::StartingStyle(..) => false,
354
355 CssRule::FontFace(..) |
356 CssRule::Namespace(..) |
357 CssRule::Style(..) |
358 CssRule::NestedDeclarations(..) |
359 CssRule::PositionTry(..) => true,
360
361 CssRule::Keyframes(..) |
362 CssRule::Page(..) |
363 CssRule::Margin(..) |
364 CssRule::Property(..) |
365 CssRule::FontFeatureValues(..) |
366 CssRule::FontPaletteValues(..) |
367 CssRule::CounterStyle(..) => !is_standard,
368 }
369 }
370}
371
372#[derive(Debug)]
374pub struct SanitizationData {
375 kind: SanitizationKind,
376 output: String,
377}
378
379impl SanitizationData {
380 #[inline]
382 pub fn new(kind: SanitizationKind) -> Option<Self> {
383 if matches!(kind, SanitizationKind::None) {
384 return None;
385 }
386 Some(Self {
387 kind,
388 output: String::new(),
389 })
390 }
391
392 #[inline]
394 pub fn take(self) -> String {
395 self.output
396 }
397}
398
399impl Stylesheet {
400 pub fn update_from_str(
402 existing: &Stylesheet,
403 css: &str,
404 url_data: UrlExtraData,
405 stylesheet_loader: Option<&dyn StylesheetLoader>,
406 error_reporter: Option<&dyn ParseErrorReporter>,
407 allow_import_rules: AllowImportRules,
408 ) {
409 let use_counters = UseCounters::default();
410 let (namespaces, rules, source_map_url, source_url) = Self::parse_rules(
411 css,
412 &url_data,
413 existing.contents.origin,
414 &existing.shared_lock,
415 stylesheet_loader,
416 error_reporter,
417 existing.contents.quirks_mode,
418 Some(&use_counters),
419 allow_import_rules,
420 None,
421 );
422
423 *existing.contents.url_data.write() = url_data;
424 *existing.contents.namespaces.write() = namespaces;
425
426 let mut guard = existing.shared_lock.write();
428 *existing.contents.rules.write_with(&mut guard) = CssRules(rules);
429 *existing.contents.source_map_url.write() = source_map_url;
430 *existing.contents.source_url.write() = source_url;
431 existing.contents.use_counters.merge(&use_counters);
432 }
433
434 fn parse_rules(
435 css: &str,
436 url_data: &UrlExtraData,
437 origin: Origin,
438 shared_lock: &SharedRwLock,
439 stylesheet_loader: Option<&dyn StylesheetLoader>,
440 error_reporter: Option<&dyn ParseErrorReporter>,
441 quirks_mode: QuirksMode,
442 use_counters: Option<&UseCounters>,
443 allow_import_rules: AllowImportRules,
444 mut sanitization_data: Option<&mut SanitizationData>,
445 ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
446 let mut input = ParserInput::new(css);
447 let mut input = Parser::new(&mut input);
448
449 let context = ParserContext::new(
450 origin,
451 url_data,
452 None,
453 ParsingMode::DEFAULT,
454 quirks_mode,
455 Default::default(),
456 error_reporter,
457 use_counters,
458 );
459
460 let mut rule_parser = TopLevelRuleParser {
461 shared_lock,
462 loader: stylesheet_loader,
463 context,
464 state: State::Start,
465 dom_error: None,
466 insert_rule_context: None,
467 allow_import_rules,
468 declaration_parser_state: Default::default(),
469 first_declaration_block: Default::default(),
470 wants_first_declaration_block: false,
471 error_reporting_state: Default::default(),
472 rules: Vec::new(),
473 };
474
475 {
476 let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
477 while let Some(result) = iter.next() {
478 match result {
479 Ok(rule_start) => {
480 if let Some(ref mut data) = sanitization_data {
482 if let Some(ref rule) = iter.parser.rules.last() {
483 if !data.kind.allows(rule) {
484 iter.parser.rules.pop();
485 continue;
486 }
487 }
488 let end = iter.input.position().byte_index();
489 data.output.push_str(&css[rule_start.byte_index()..end]);
490 }
491 },
492 Err((error, slice)) => {
493 let location = error.location;
494 let error = ContextualParseError::InvalidRule(slice, error);
495 iter.parser.context.log_css_error(location, error);
496 },
497 }
498 }
499 }
500
501 let source_map_url = input.current_source_map_url().map(String::from);
502 let source_url = input.current_source_url().map(String::from);
503 (
504 rule_parser.context.namespaces.into_owned(),
505 rule_parser.rules,
506 source_map_url,
507 source_url,
508 )
509 }
510
511 pub fn from_str(
517 css: &str,
518 url_data: UrlExtraData,
519 origin: Origin,
520 media: Arc<Locked<MediaList>>,
521 shared_lock: SharedRwLock,
522 stylesheet_loader: Option<&dyn StylesheetLoader>,
523 error_reporter: Option<&dyn ParseErrorReporter>,
524 quirks_mode: QuirksMode,
525 allow_import_rules: AllowImportRules,
526 ) -> Self {
527 let contents = StylesheetContents::from_str(
529 css,
530 url_data,
531 origin,
532 &shared_lock,
533 stylesheet_loader,
534 error_reporter,
535 quirks_mode,
536 allow_import_rules,
537 None,
538 );
539
540 Stylesheet {
541 contents,
542 shared_lock,
543 media,
544 disabled: AtomicBool::new(false),
545 }
546 }
547
548 pub fn disabled(&self) -> bool {
551 self.disabled.load(Ordering::SeqCst)
552 }
553
554 pub fn set_disabled(&self, disabled: bool) -> bool {
562 self.disabled.swap(disabled, Ordering::SeqCst) != disabled
563 }
564}
565
566#[cfg(feature = "servo")]
567impl Clone for Stylesheet {
568 fn clone(&self) -> Self {
569 let lock = self.shared_lock.clone();
571 let guard = self.shared_lock.read();
572
573 let media = self.media.read_with(&guard).clone();
575 let media = Arc::new(lock.wrap(media));
576 let contents = Arc::new(self.contents.deep_clone_with_lock(&lock, &guard));
577
578 Stylesheet {
579 contents,
580 media,
581 shared_lock: lock,
582 disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
583 }
584 }
585}