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
32#[derive(Clone, Debug, Default, MallocSizeOf)]
36#[allow(missing_docs)]
37pub struct Namespaces {
38 pub default: Option<Namespace>,
39 pub prefixes: FxHashMap<Prefix, Namespace>,
40}
41
42#[derive(Debug)]
45pub struct StylesheetContents {
46 pub rules: Arc<Locked<CssRules>>,
49 pub origin: Origin,
51 pub url_data: UrlExtraData,
53 pub namespaces: Namespaces,
55 pub quirks_mode: QuirksMode,
57 pub source_map_url: Option<String>,
59 pub source_url: Option<String>,
61 pub use_counters: UseCounters,
63
64 _forbid_construction: (),
67}
68
69impl StylesheetContents {
70 pub fn from_str(
73 css: &str,
74 url_data: UrlExtraData,
75 origin: Origin,
76 shared_lock: &SharedRwLock,
77 stylesheet_loader: Option<&dyn StylesheetLoader>,
78 error_reporter: Option<&dyn ParseErrorReporter>,
79 quirks_mode: QuirksMode,
80 allow_import_rules: AllowImportRules,
81 sanitization_data: Option<&mut SanitizationData>,
82 ) -> Arc<Self> {
83 let use_counters = UseCounters::default();
84 let (namespaces, rules, source_map_url, source_url) = Stylesheet::parse_rules(
85 css,
86 &url_data,
87 origin,
88 &shared_lock,
89 stylesheet_loader,
90 error_reporter,
91 quirks_mode,
92 Some(&use_counters),
93 allow_import_rules,
94 sanitization_data,
95 );
96
97 Arc::new(Self {
98 rules: CssRules::new(rules, &shared_lock),
99 origin,
100 url_data,
101 namespaces,
102 quirks_mode,
103 source_map_url,
104 source_url,
105 use_counters,
106 _forbid_construction: (),
107 })
108 }
109
110 pub fn from_shared_data(
122 rules: Arc<Locked<CssRules>>,
123 origin: Origin,
124 url_data: UrlExtraData,
125 quirks_mode: QuirksMode,
126 ) -> Arc<Self> {
127 debug_assert!(rules.is_static());
128 Arc::new(Self {
129 rules,
130 origin,
131 url_data,
132 namespaces: Namespaces::default(),
133 quirks_mode,
134 source_map_url: None,
135 source_url: None,
136 use_counters: UseCounters::default(),
137 _forbid_construction: (),
138 })
139 }
140
141 #[inline]
143 pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
144 &self.rules.read_with(guard).0
145 }
146
147 #[cfg(feature = "gecko")]
149 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
150 if self.rules.is_static() {
151 return 0;
152 }
153 self.rules.unconditional_shallow_size_of(ops)
155 + self.rules.read_with(guard).size_of(guard, ops)
156 }
157
158 #[inline]
160 pub fn iter_rules<'a, 'b, C, CMM>(
161 &'a self,
162 device: &'a Device,
163 custom_media: CMM,
164 guard: &'a SharedRwLockReadGuard<'b>,
165 ) -> RulesIterator<'a, 'b, C, CMM>
166 where
167 C: NestedRuleIterationCondition,
168 CMM: Deref<Target = CustomMediaMap>,
169 {
170 RulesIterator::new(
171 device,
172 self.quirks_mode,
173 custom_media,
174 guard,
175 self.rules(guard).iter(),
176 )
177 }
178
179 #[inline]
182 pub fn effective_rules<'a, 'b, CMM: Deref<Target = CustomMediaMap>>(
183 &'a self,
184 device: &'a Device,
185 custom_media: CMM,
186 guard: &'a SharedRwLockReadGuard<'b>,
187 ) -> EffectiveRulesIterator<'a, 'b, CMM> {
188 self.iter_rules::<EffectiveRules, CMM>(device, custom_media, guard)
189 }
190
191 pub fn deep_clone(
193 &self,
194 lock: &SharedRwLock,
195 url_data: Option<&UrlExtraData>,
196 guard: &SharedRwLockReadGuard,
197 ) -> Arc<Self> {
198 let rules = self
200 .rules
201 .read_with(guard)
202 .deep_clone_with_lock(lock, guard);
203
204 let url_data = url_data.cloned().unwrap_or_else(|| self.url_data.clone());
205
206 Arc::new(Self {
207 rules: Arc::new(lock.wrap(rules)),
208 quirks_mode: self.quirks_mode,
209 origin: self.origin,
210 url_data,
211 namespaces: self.namespaces.clone(),
212 source_map_url: self.source_map_url.clone(),
213 source_url: self.source_url.clone(),
214 use_counters: self.use_counters.clone(),
215 _forbid_construction: (),
216 })
217 }
218}
219
220#[derive(Debug)]
222pub struct Stylesheet {
223 pub contents: Locked<Arc<StylesheetContents>>,
225 pub shared_lock: SharedRwLock,
227 pub media: Arc<Locked<MediaList>>,
229 pub disabled: AtomicBool,
231}
232
233pub trait StylesheetInDocument: ::std::fmt::Debug {
235 fn enabled(&self) -> bool;
237
238 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
240
241 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents;
243
244 fn is_effective_for_device(
246 &self,
247 device: &Device,
248 custom_media: &CustomMediaMap,
249 guard: &SharedRwLockReadGuard,
250 ) -> bool {
251 let media = match self.media(guard) {
252 Some(m) => m,
253 None => return true,
254 };
255 media.evaluate(
256 device,
257 self.contents(guard).quirks_mode,
258 &mut CustomMediaEvaluator::new(custom_media, guard),
259 )
260 }
261
262 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot>;
264}
265
266impl StylesheetInDocument for Stylesheet {
267 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
268 Some(self.media.read_with(guard))
269 }
270
271 fn enabled(&self) -> bool {
272 !self.disabled()
273 }
274
275 #[inline]
276 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
277 self.contents.read_with(guard)
278 }
279
280 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
281 None
282 }
283}
284
285#[derive(Clone, Debug)]
288#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
289pub struct DocumentStyleSheet(
290 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
291);
292
293impl PartialEq for DocumentStyleSheet {
294 fn eq(&self, other: &Self) -> bool {
295 Arc::ptr_eq(&self.0, &other.0)
296 }
297}
298
299impl StylesheetInDocument for DocumentStyleSheet {
300 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
301 self.0.media(guard)
302 }
303
304 fn enabled(&self) -> bool {
305 self.0.enabled()
306 }
307
308 #[inline]
309 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
310 self.0.contents(guard)
311 }
312
313 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
314 None
315 }
316}
317
318#[repr(u8)]
320#[derive(Clone, Copy, Debug, PartialEq)]
321pub enum SanitizationKind {
322 None,
324 Standard,
326 NoConditionalRules,
328}
329
330#[repr(u8)]
332#[derive(Clone, Copy, Debug, PartialEq)]
333pub enum AllowImportRules {
334 Yes,
336 No,
338}
339
340impl SanitizationKind {
341 fn allows(self, rule: &CssRule) -> bool {
342 debug_assert_ne!(self, SanitizationKind::None);
343 let is_standard = matches!(self, SanitizationKind::Standard);
347 match *rule {
348 CssRule::Document(..) |
349 CssRule::Media(..) |
350 CssRule::CustomMedia(..) |
351 CssRule::Supports(..) |
352 CssRule::Import(..) |
353 CssRule::Container(..) |
354 CssRule::LayerStatement(..) |
357 CssRule::LayerBlock(..) |
358 CssRule::Scope(..) |
361 CssRule::StartingStyle(..) => false,
362
363 CssRule::FontFace(..) |
364 CssRule::Namespace(..) |
365 CssRule::Style(..) |
366 CssRule::NestedDeclarations(..) |
367 CssRule::PositionTry(..) => true,
368
369 CssRule::Keyframes(..) |
370 CssRule::Page(..) |
371 CssRule::Margin(..) |
372 CssRule::Property(..) |
373 CssRule::FontFeatureValues(..) |
374 CssRule::FontPaletteValues(..) |
375 CssRule::CounterStyle(..) => !is_standard,
376 }
377 }
378}
379
380#[derive(Debug)]
382pub struct SanitizationData {
383 kind: SanitizationKind,
384 output: String,
385}
386
387impl SanitizationData {
388 #[inline]
390 pub fn new(kind: SanitizationKind) -> Option<Self> {
391 if matches!(kind, SanitizationKind::None) {
392 return None;
393 }
394 Some(Self {
395 kind,
396 output: String::new(),
397 })
398 }
399
400 #[inline]
402 pub fn take(self) -> String {
403 self.output
404 }
405}
406
407impl Stylesheet {
408 fn parse_rules(
409 css: &str,
410 url_data: &UrlExtraData,
411 origin: Origin,
412 shared_lock: &SharedRwLock,
413 stylesheet_loader: Option<&dyn StylesheetLoader>,
414 error_reporter: Option<&dyn ParseErrorReporter>,
415 quirks_mode: QuirksMode,
416 use_counters: Option<&UseCounters>,
417 allow_import_rules: AllowImportRules,
418 mut sanitization_data: Option<&mut SanitizationData>,
419 ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
420 let mut input = ParserInput::new(css);
421 let mut input = Parser::new(&mut input);
422
423 let context = ParserContext::new(
424 origin,
425 url_data,
426 None,
427 ParsingMode::DEFAULT,
428 quirks_mode,
429 Default::default(),
430 error_reporter,
431 use_counters,
432 );
433
434 let mut rule_parser = TopLevelRuleParser {
435 shared_lock,
436 loader: stylesheet_loader,
437 context,
438 state: State::Start,
439 dom_error: None,
440 insert_rule_context: None,
441 allow_import_rules,
442 declaration_parser_state: Default::default(),
443 first_declaration_block: Default::default(),
444 wants_first_declaration_block: false,
445 error_reporting_state: Default::default(),
446 rules: Vec::new(),
447 };
448
449 {
450 let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
451 while let Some(result) = iter.next() {
452 match result {
453 Ok(rule_start) => {
454 if let Some(ref mut data) = sanitization_data {
456 if let Some(ref rule) = iter.parser.rules.last() {
457 if !data.kind.allows(rule) {
458 iter.parser.rules.pop();
459 continue;
460 }
461 }
462 let end = iter.input.position().byte_index();
463 data.output.push_str(&css[rule_start.byte_index()..end]);
464 }
465 },
466 Err((error, slice)) => {
467 let location = error.location;
468 let error = ContextualParseError::InvalidRule(slice, error);
469 iter.parser.context.log_css_error(location, error);
470 },
471 }
472 }
473 }
474
475 let source_map_url = input.current_source_map_url().map(String::from);
476 let source_url = input.current_source_url().map(String::from);
477 (
478 rule_parser.context.namespaces.into_owned(),
479 rule_parser.rules,
480 source_map_url,
481 source_url,
482 )
483 }
484
485 pub fn from_str(
487 css: &str,
488 url_data: UrlExtraData,
489 origin: Origin,
490 media: Arc<Locked<MediaList>>,
491 shared_lock: SharedRwLock,
492 stylesheet_loader: Option<&dyn StylesheetLoader>,
493 error_reporter: Option<&dyn ParseErrorReporter>,
494 quirks_mode: QuirksMode,
495 allow_import_rules: AllowImportRules,
496 ) -> Self {
497 let contents = StylesheetContents::from_str(
499 css,
500 url_data,
501 origin,
502 &shared_lock,
503 stylesheet_loader,
504 error_reporter,
505 quirks_mode,
506 allow_import_rules,
507 None,
508 );
509
510 Stylesheet {
511 contents: shared_lock.wrap(contents),
512 shared_lock,
513 media,
514 disabled: AtomicBool::new(false),
515 }
516 }
517
518 pub fn disabled(&self) -> bool {
521 self.disabled.load(Ordering::SeqCst)
522 }
523
524 pub fn set_disabled(&self, disabled: bool) -> bool {
532 self.disabled.swap(disabled, Ordering::SeqCst) != disabled
533 }
534}
535
536#[cfg(feature = "servo")]
537impl Clone for Stylesheet {
538 fn clone(&self) -> Self {
539 let lock = self.shared_lock.clone();
541 let guard = self.shared_lock.read();
542
543 let media = self.media.read_with(&guard).clone();
545 let media = Arc::new(lock.wrap(media));
546 let contents = lock.wrap(
547 self.contents
548 .read_with(&guard)
549 .deep_clone(&lock, None, &guard),
550 );
551
552 Stylesheet {
553 contents,
554 media,
555 shared_lock: lock,
556 disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
557 }
558 }
559}