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