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