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::{
16 CssRule, CssRules, CustomMediaEvaluator, CustomMediaMap, Origin, UrlExtraData,
17};
18use crate::use_counters::UseCounters;
19use crate::{Namespace, Prefix};
20use cssparser::{Parser, ParserInput, StyleSheetParser};
21#[cfg(feature = "gecko")]
22use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
23use rustc_hash::FxHashMap;
24use servo_arc::Arc;
25use std::ops::Deref;
26use std::sync::atomic::{AtomicBool, Ordering};
27use style_traits::ParsingMode;
28
29use super::scope_rule::ImplicitScopeRoot;
30
31pub struct UserAgentStylesheets {
33 pub shared_lock: SharedRwLock,
35 pub user_or_user_agent_stylesheets: Vec<DocumentStyleSheet>,
37 pub quirks_mode_stylesheet: DocumentStyleSheet,
39}
40
41#[derive(Clone, Debug, Default, MallocSizeOf)]
45#[allow(missing_docs)]
46pub struct Namespaces {
47 pub default: Option<Namespace>,
48 pub prefixes: FxHashMap<Prefix, Namespace>,
49}
50
51#[derive(Debug)]
54pub struct StylesheetContents {
55 pub rules: Arc<Locked<CssRules>>,
58 pub origin: Origin,
60 pub url_data: UrlExtraData,
62 pub namespaces: Namespaces,
64 pub quirks_mode: QuirksMode,
66 pub source_map_url: Option<String>,
68 pub source_url: Option<String>,
70 pub use_counters: UseCounters,
72
73 _forbid_construction: (),
76}
77
78impl StylesheetContents {
79 pub fn from_str(
82 css: &str,
83 url_data: UrlExtraData,
84 origin: Origin,
85 shared_lock: &SharedRwLock,
86 stylesheet_loader: Option<&dyn StylesheetLoader>,
87 error_reporter: Option<&dyn ParseErrorReporter>,
88 quirks_mode: QuirksMode,
89 allow_import_rules: AllowImportRules,
90 sanitization_data: Option<&mut SanitizationData>,
91 ) -> Arc<Self> {
92 let use_counters = UseCounters::default();
93 let (namespaces, rules, source_map_url, source_url) = Stylesheet::parse_rules(
94 css,
95 &url_data,
96 origin,
97 &shared_lock,
98 stylesheet_loader,
99 error_reporter,
100 quirks_mode,
101 Some(&use_counters),
102 allow_import_rules,
103 sanitization_data,
104 );
105
106 Arc::new(Self {
107 rules: CssRules::new(rules, &shared_lock),
108 origin,
109 url_data,
110 namespaces,
111 quirks_mode,
112 source_map_url,
113 source_url,
114 use_counters,
115 _forbid_construction: (),
116 })
117 }
118
119 pub fn from_shared_data(
131 rules: Arc<Locked<CssRules>>,
132 origin: Origin,
133 url_data: UrlExtraData,
134 quirks_mode: QuirksMode,
135 ) -> Arc<Self> {
136 debug_assert!(rules.is_static());
137 Arc::new(Self {
138 rules,
139 origin,
140 url_data,
141 namespaces: Namespaces::default(),
142 quirks_mode,
143 source_map_url: None,
144 source_url: None,
145 use_counters: UseCounters::default(),
146 _forbid_construction: (),
147 })
148 }
149
150 #[inline]
152 pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
153 &self.rules.read_with(guard).0
154 }
155
156 #[cfg(feature = "gecko")]
158 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
159 if self.rules.is_static() {
160 return 0;
161 }
162 self.rules.unconditional_shallow_size_of(ops)
164 + self.rules.read_with(guard).size_of(guard, ops)
165 }
166
167 #[inline]
169 pub fn iter_rules<'a, 'b, C, CMM>(
170 &'a self,
171 device: &'a Device,
172 custom_media: CMM,
173 guard: &'a SharedRwLockReadGuard<'b>,
174 ) -> RulesIterator<'a, 'b, C, CMM>
175 where
176 C: NestedRuleIterationCondition,
177 CMM: Deref<Target = CustomMediaMap>,
178 {
179 RulesIterator::new(
180 device,
181 self.quirks_mode,
182 custom_media,
183 guard,
184 self.rules(guard).iter(),
185 )
186 }
187
188 #[inline]
191 pub fn effective_rules<'a, 'b, CMM: Deref<Target = CustomMediaMap>>(
192 &'a self,
193 device: &'a Device,
194 custom_media: CMM,
195 guard: &'a SharedRwLockReadGuard<'b>,
196 ) -> EffectiveRulesIterator<'a, 'b, CMM> {
197 self.iter_rules::<EffectiveRules, CMM>(device, custom_media, guard)
198 }
199
200 pub fn deep_clone(
202 &self,
203 lock: &SharedRwLock,
204 url_data: Option<&UrlExtraData>,
205 guard: &SharedRwLockReadGuard,
206 ) -> Arc<Self> {
207 let rules = self
209 .rules
210 .read_with(guard)
211 .deep_clone_with_lock(lock, guard);
212
213 let url_data = url_data.cloned().unwrap_or_else(|| self.url_data.clone());
214
215 Arc::new(Self {
216 rules: Arc::new(lock.wrap(rules)),
217 quirks_mode: self.quirks_mode,
218 origin: self.origin,
219 url_data,
220 namespaces: self.namespaces.clone(),
221 source_map_url: self.source_map_url.clone(),
222 source_url: self.source_url.clone(),
223 use_counters: self.use_counters.clone(),
224 _forbid_construction: (),
225 })
226 }
227}
228
229#[derive(Debug)]
231pub struct Stylesheet {
232 pub contents: Locked<Arc<StylesheetContents>>,
234 pub shared_lock: SharedRwLock,
236 pub media: Arc<Locked<MediaList>>,
238 pub disabled: AtomicBool,
240}
241
242pub trait StylesheetInDocument: ::std::fmt::Debug {
244 fn enabled(&self) -> bool;
246
247 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
249
250 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents;
252
253 fn is_effective_for_device(
255 &self,
256 device: &Device,
257 custom_media: &CustomMediaMap,
258 guard: &SharedRwLockReadGuard,
259 ) -> bool {
260 let media = match self.media(guard) {
261 Some(m) => m,
262 None => return true,
263 };
264 media.evaluate(
265 device,
266 self.contents(guard).quirks_mode,
267 &mut CustomMediaEvaluator::new(custom_media, guard),
268 )
269 }
270
271 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot>;
273}
274
275impl StylesheetInDocument for Stylesheet {
276 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
277 Some(self.media.read_with(guard))
278 }
279
280 fn enabled(&self) -> bool {
281 !self.disabled()
282 }
283
284 #[inline]
285 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
286 self.contents.read_with(guard)
287 }
288
289 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
290 None
291 }
292}
293
294#[derive(Clone, Debug)]
297#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
298pub struct DocumentStyleSheet(
299 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
300);
301
302impl PartialEq for DocumentStyleSheet {
303 fn eq(&self, other: &Self) -> bool {
304 Arc::ptr_eq(&self.0, &other.0)
305 }
306}
307
308impl StylesheetInDocument for DocumentStyleSheet {
309 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
310 self.0.media(guard)
311 }
312
313 fn enabled(&self) -> bool {
314 self.0.enabled()
315 }
316
317 #[inline]
318 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
319 self.0.contents(guard)
320 }
321
322 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
323 None
324 }
325}
326
327#[repr(u8)]
329#[derive(Clone, Copy, Debug, PartialEq)]
330pub enum SanitizationKind {
331 None,
333 Standard,
335 NoConditionalRules,
337}
338
339#[repr(u8)]
341#[derive(Clone, Copy, Debug, PartialEq)]
342pub enum AllowImportRules {
343 Yes,
345 No,
347}
348
349impl SanitizationKind {
350 fn allows(self, rule: &CssRule) -> bool {
351 debug_assert_ne!(self, SanitizationKind::None);
352 let is_standard = matches!(self, SanitizationKind::Standard);
356 match *rule {
357 CssRule::Document(..) |
358 CssRule::Media(..) |
359 CssRule::CustomMedia(..) |
360 CssRule::Supports(..) |
361 CssRule::Import(..) |
362 CssRule::Container(..) |
363 CssRule::LayerStatement(..) |
366 CssRule::LayerBlock(..) |
367 CssRule::Scope(..) |
370 CssRule::StartingStyle(..) => false,
371
372 CssRule::FontFace(..) |
373 CssRule::Namespace(..) |
374 CssRule::Style(..) |
375 CssRule::NestedDeclarations(..) |
376 CssRule::PositionTry(..) => true,
377
378 CssRule::Keyframes(..) |
379 CssRule::Page(..) |
380 CssRule::Margin(..) |
381 CssRule::Property(..) |
382 CssRule::FontFeatureValues(..) |
383 CssRule::FontPaletteValues(..) |
384 CssRule::CounterStyle(..) => !is_standard,
385 }
386 }
387}
388
389#[derive(Debug)]
391pub struct SanitizationData {
392 kind: SanitizationKind,
393 output: String,
394}
395
396impl SanitizationData {
397 #[inline]
399 pub fn new(kind: SanitizationKind) -> Option<Self> {
400 if matches!(kind, SanitizationKind::None) {
401 return None;
402 }
403 Some(Self {
404 kind,
405 output: String::new(),
406 })
407 }
408
409 #[inline]
411 pub fn take(self) -> String {
412 self.output
413 }
414}
415
416impl Stylesheet {
417 fn parse_rules(
418 css: &str,
419 url_data: &UrlExtraData,
420 origin: Origin,
421 shared_lock: &SharedRwLock,
422 stylesheet_loader: Option<&dyn StylesheetLoader>,
423 error_reporter: Option<&dyn ParseErrorReporter>,
424 quirks_mode: QuirksMode,
425 use_counters: Option<&UseCounters>,
426 allow_import_rules: AllowImportRules,
427 mut sanitization_data: Option<&mut SanitizationData>,
428 ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
429 let mut input = ParserInput::new(css);
430 let mut input = Parser::new(&mut input);
431
432 let context = ParserContext::new(
433 origin,
434 url_data,
435 None,
436 ParsingMode::DEFAULT,
437 quirks_mode,
438 Default::default(),
439 error_reporter,
440 use_counters,
441 );
442
443 let mut rule_parser = TopLevelRuleParser {
444 shared_lock,
445 loader: stylesheet_loader,
446 context,
447 state: State::Start,
448 dom_error: None,
449 insert_rule_context: None,
450 allow_import_rules,
451 declaration_parser_state: Default::default(),
452 first_declaration_block: Default::default(),
453 wants_first_declaration_block: false,
454 error_reporting_state: Default::default(),
455 rules: Vec::new(),
456 };
457
458 {
459 let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
460 while let Some(result) = iter.next() {
461 match result {
462 Ok(rule_start) => {
463 if let Some(ref mut data) = sanitization_data {
465 if let Some(ref rule) = iter.parser.rules.last() {
466 if !data.kind.allows(rule) {
467 iter.parser.rules.pop();
468 continue;
469 }
470 }
471 let end = iter.input.position().byte_index();
472 data.output.push_str(&css[rule_start.byte_index()..end]);
473 }
474 },
475 Err((error, slice)) => {
476 let location = error.location;
477 let error = ContextualParseError::InvalidRule(slice, error);
478 iter.parser.context.log_css_error(location, error);
479 },
480 }
481 }
482 }
483
484 let source_map_url = input.current_source_map_url().map(String::from);
485 let source_url = input.current_source_url().map(String::from);
486 (
487 rule_parser.context.namespaces.into_owned(),
488 rule_parser.rules,
489 source_map_url,
490 source_url,
491 )
492 }
493
494 pub fn from_str(
496 css: &str,
497 url_data: UrlExtraData,
498 origin: Origin,
499 media: Arc<Locked<MediaList>>,
500 shared_lock: SharedRwLock,
501 stylesheet_loader: Option<&dyn StylesheetLoader>,
502 error_reporter: Option<&dyn ParseErrorReporter>,
503 quirks_mode: QuirksMode,
504 allow_import_rules: AllowImportRules,
505 ) -> Self {
506 let contents = StylesheetContents::from_str(
508 css,
509 url_data,
510 origin,
511 &shared_lock,
512 stylesheet_loader,
513 error_reporter,
514 quirks_mode,
515 allow_import_rules,
516 None,
517 );
518
519 Stylesheet {
520 contents: shared_lock.wrap(contents),
521 shared_lock,
522 media,
523 disabled: AtomicBool::new(false),
524 }
525 }
526
527 pub fn disabled(&self) -> bool {
530 self.disabled.load(Ordering::SeqCst)
531 }
532
533 pub fn set_disabled(&self, disabled: bool) -> bool {
541 self.disabled.swap(disabled, Ordering::SeqCst) != disabled
542 }
543}
544
545#[cfg(feature = "servo")]
546impl Clone for Stylesheet {
547 fn clone(&self) -> Self {
548 let lock = self.shared_lock.clone();
550 let guard = self.shared_lock.read();
551
552 let media = self.media.read_with(&guard).clone();
554 let media = Arc::new(lock.wrap(media));
555 let contents = lock.wrap(
556 self.contents
557 .read_with(&guard)
558 .deep_clone(&lock, None, &guard),
559 );
560
561 Stylesheet {
562 contents,
563 media,
564 shared_lock: lock,
565 disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
566 }
567 }
568}