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 rustc_hash::FxHashMap;
22use servo_arc::Arc;
23use std::sync::atomic::{AtomicBool, Ordering};
24use style_traits::ParsingMode;
25
26use super::scope_rule::ImplicitScopeRoot;
27
28pub struct UserAgentStylesheets {
30 pub shared_lock: SharedRwLock,
32 pub user_or_user_agent_stylesheets: Vec<DocumentStyleSheet>,
34 pub quirks_mode_stylesheet: DocumentStyleSheet,
36}
37
38#[derive(Clone, Debug, Default, MallocSizeOf)]
42#[allow(missing_docs)]
43pub struct Namespaces {
44 pub default: Option<Namespace>,
45 pub prefixes: FxHashMap<Prefix, Namespace>,
46}
47
48#[derive(Debug)]
51pub struct StylesheetContents {
52 pub rules: Arc<Locked<CssRules>>,
55 pub origin: Origin,
57 pub url_data: UrlExtraData,
59 pub namespaces: Namespaces,
61 pub quirks_mode: QuirksMode,
63 pub source_map_url: Option<String>,
65 pub source_url: Option<String>,
67 pub use_counters: UseCounters,
69
70 _forbid_construction: (),
73}
74
75impl StylesheetContents {
76 pub fn from_str(
79 css: &str,
80 url_data: UrlExtraData,
81 origin: Origin,
82 shared_lock: &SharedRwLock,
83 stylesheet_loader: Option<&dyn StylesheetLoader>,
84 error_reporter: Option<&dyn ParseErrorReporter>,
85 quirks_mode: QuirksMode,
86 allow_import_rules: AllowImportRules,
87 sanitization_data: Option<&mut SanitizationData>,
88 ) -> Arc<Self> {
89 let use_counters = UseCounters::default();
90 let (namespaces, rules, source_map_url, source_url) = Stylesheet::parse_rules(
91 css,
92 &url_data,
93 origin,
94 &shared_lock,
95 stylesheet_loader,
96 error_reporter,
97 quirks_mode,
98 Some(&use_counters),
99 allow_import_rules,
100 sanitization_data,
101 );
102
103 Arc::new(Self {
104 rules: CssRules::new(rules, &shared_lock),
105 origin,
106 url_data,
107 namespaces,
108 quirks_mode,
109 source_map_url,
110 source_url,
111 use_counters,
112 _forbid_construction: (),
113 })
114 }
115
116 pub fn from_shared_data(
128 rules: Arc<Locked<CssRules>>,
129 origin: Origin,
130 url_data: UrlExtraData,
131 quirks_mode: QuirksMode,
132 ) -> Arc<Self> {
133 debug_assert!(rules.is_static());
134 Arc::new(Self {
135 rules,
136 origin,
137 url_data,
138 namespaces: Namespaces::default(),
139 quirks_mode,
140 source_map_url: None,
141 source_url: None,
142 use_counters: UseCounters::default(),
143 _forbid_construction: (),
144 })
145 }
146
147 #[inline]
149 pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
150 &self.rules.read_with(guard).0
151 }
152
153 #[cfg(feature = "gecko")]
155 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
156 if self.rules.is_static() {
157 return 0;
158 }
159 self.rules.unconditional_shallow_size_of(ops)
161 + self.rules.read_with(guard).size_of(guard, ops)
162 }
163
164 #[inline]
166 pub fn iter_rules<'a, 'b, C>(
167 &'a self,
168 device: &'a Device,
169 guard: &'a SharedRwLockReadGuard<'b>,
170 ) -> RulesIterator<'a, 'b, C>
171 where
172 C: NestedRuleIterationCondition,
173 {
174 RulesIterator::new(device, self.quirks_mode, guard, self.rules(guard).iter())
175 }
176
177 #[inline]
180 pub fn effective_rules<'a, 'b>(
181 &'a self,
182 device: &'a Device,
183 guard: &'a SharedRwLockReadGuard<'b>,
184 ) -> EffectiveRulesIterator<'a, 'b> {
185 self.iter_rules::<EffectiveRules>(device, guard)
186 }
187}
188
189impl DeepCloneWithLock for StylesheetContents {
190 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
191 let rules = self
193 .rules
194 .read_with(guard)
195 .deep_clone_with_lock(lock, guard);
196
197 Self {
198 rules: Arc::new(lock.wrap(rules)),
199 quirks_mode: self.quirks_mode,
200 origin: self.origin,
201 url_data: self.url_data.clone(),
202 namespaces: self.namespaces.clone(),
203 source_map_url: self.source_map_url.clone(),
204 source_url: self.source_url.clone(),
205 use_counters: self.use_counters.clone(),
206 _forbid_construction: (),
207 }
208 }
209}
210
211#[derive(Debug)]
213pub struct Stylesheet {
214 pub contents: Locked<Arc<StylesheetContents>>,
216 pub shared_lock: SharedRwLock,
218 pub media: Arc<Locked<MediaList>>,
220 pub disabled: AtomicBool,
222}
223
224pub trait StylesheetInDocument: ::std::fmt::Debug {
226 fn enabled(&self) -> bool;
228
229 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
231
232 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents;
234
235 fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
237 match self.media(guard) {
238 Some(medialist) => medialist.evaluate(device, self.contents(guard).quirks_mode),
239 None => true,
240 }
241 }
242
243 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot>;
245}
246
247impl StylesheetInDocument for Stylesheet {
248 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
249 Some(self.media.read_with(guard))
250 }
251
252 fn enabled(&self) -> bool {
253 !self.disabled()
254 }
255
256 #[inline]
257 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
258 self.contents.read_with(guard)
259 }
260
261 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
262 None
263 }
264}
265
266#[derive(Clone, Debug)]
269#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
270pub struct DocumentStyleSheet(
271 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
272);
273
274impl PartialEq for DocumentStyleSheet {
275 fn eq(&self, other: &Self) -> bool {
276 Arc::ptr_eq(&self.0, &other.0)
277 }
278}
279
280impl StylesheetInDocument for DocumentStyleSheet {
281 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
282 self.0.media(guard)
283 }
284
285 fn enabled(&self) -> bool {
286 self.0.enabled()
287 }
288
289 #[inline]
290 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
291 self.0.contents(guard)
292 }
293
294 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
295 None
296 }
297}
298
299#[repr(u8)]
301#[derive(Clone, Copy, Debug, PartialEq)]
302pub enum SanitizationKind {
303 None,
305 Standard,
307 NoConditionalRules,
309}
310
311#[repr(u8)]
313#[derive(Clone, Copy, Debug, PartialEq)]
314pub enum AllowImportRules {
315 Yes,
317 No,
319}
320
321impl SanitizationKind {
322 fn allows(self, rule: &CssRule) -> bool {
323 debug_assert_ne!(self, SanitizationKind::None);
324 let is_standard = matches!(self, SanitizationKind::Standard);
328 match *rule {
329 CssRule::Document(..) |
330 CssRule::Media(..) |
331 CssRule::Supports(..) |
332 CssRule::Import(..) |
333 CssRule::Container(..) |
334 CssRule::LayerStatement(..) |
337 CssRule::LayerBlock(..) |
338 CssRule::Scope(..) |
341 CssRule::StartingStyle(..) => false,
342
343 CssRule::FontFace(..) |
344 CssRule::Namespace(..) |
345 CssRule::Style(..) |
346 CssRule::NestedDeclarations(..) |
347 CssRule::PositionTry(..) => true,
348
349 CssRule::Keyframes(..) |
350 CssRule::Page(..) |
351 CssRule::Margin(..) |
352 CssRule::Property(..) |
353 CssRule::FontFeatureValues(..) |
354 CssRule::FontPaletteValues(..) |
355 CssRule::CounterStyle(..) => !is_standard,
356 }
357 }
358}
359
360#[derive(Debug)]
362pub struct SanitizationData {
363 kind: SanitizationKind,
364 output: String,
365}
366
367impl SanitizationData {
368 #[inline]
370 pub fn new(kind: SanitizationKind) -> Option<Self> {
371 if matches!(kind, SanitizationKind::None) {
372 return None;
373 }
374 Some(Self {
375 kind,
376 output: String::new(),
377 })
378 }
379
380 #[inline]
382 pub fn take(self) -> String {
383 self.output
384 }
385}
386
387impl Stylesheet {
388 fn parse_rules(
389 css: &str,
390 url_data: &UrlExtraData,
391 origin: Origin,
392 shared_lock: &SharedRwLock,
393 stylesheet_loader: Option<&dyn StylesheetLoader>,
394 error_reporter: Option<&dyn ParseErrorReporter>,
395 quirks_mode: QuirksMode,
396 use_counters: Option<&UseCounters>,
397 allow_import_rules: AllowImportRules,
398 mut sanitization_data: Option<&mut SanitizationData>,
399 ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
400 let mut input = ParserInput::new(css);
401 let mut input = Parser::new(&mut input);
402
403 let context = ParserContext::new(
404 origin,
405 url_data,
406 None,
407 ParsingMode::DEFAULT,
408 quirks_mode,
409 Default::default(),
410 error_reporter,
411 use_counters,
412 );
413
414 let mut rule_parser = TopLevelRuleParser {
415 shared_lock,
416 loader: stylesheet_loader,
417 context,
418 state: State::Start,
419 dom_error: None,
420 insert_rule_context: None,
421 allow_import_rules,
422 declaration_parser_state: Default::default(),
423 first_declaration_block: Default::default(),
424 wants_first_declaration_block: false,
425 error_reporting_state: Default::default(),
426 rules: Vec::new(),
427 };
428
429 {
430 let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
431 while let Some(result) = iter.next() {
432 match result {
433 Ok(rule_start) => {
434 if let Some(ref mut data) = sanitization_data {
436 if let Some(ref rule) = iter.parser.rules.last() {
437 if !data.kind.allows(rule) {
438 iter.parser.rules.pop();
439 continue;
440 }
441 }
442 let end = iter.input.position().byte_index();
443 data.output.push_str(&css[rule_start.byte_index()..end]);
444 }
445 },
446 Err((error, slice)) => {
447 let location = error.location;
448 let error = ContextualParseError::InvalidRule(slice, error);
449 iter.parser.context.log_css_error(location, error);
450 },
451 }
452 }
453 }
454
455 let source_map_url = input.current_source_map_url().map(String::from);
456 let source_url = input.current_source_url().map(String::from);
457 (
458 rule_parser.context.namespaces.into_owned(),
459 rule_parser.rules,
460 source_map_url,
461 source_url,
462 )
463 }
464
465 pub fn from_str(
467 css: &str,
468 url_data: UrlExtraData,
469 origin: Origin,
470 media: Arc<Locked<MediaList>>,
471 shared_lock: SharedRwLock,
472 stylesheet_loader: Option<&dyn StylesheetLoader>,
473 error_reporter: Option<&dyn ParseErrorReporter>,
474 quirks_mode: QuirksMode,
475 allow_import_rules: AllowImportRules,
476 ) -> Self {
477 let contents = StylesheetContents::from_str(
479 css,
480 url_data,
481 origin,
482 &shared_lock,
483 stylesheet_loader,
484 error_reporter,
485 quirks_mode,
486 allow_import_rules,
487 None,
488 );
489
490 Stylesheet {
491 contents: shared_lock.wrap(contents),
492 shared_lock,
493 media,
494 disabled: AtomicBool::new(false),
495 }
496 }
497
498 pub fn disabled(&self) -> bool {
501 self.disabled.load(Ordering::SeqCst)
502 }
503
504 pub fn set_disabled(&self, disabled: bool) -> bool {
512 self.disabled.swap(disabled, Ordering::SeqCst) != disabled
513 }
514}
515
516#[cfg(feature = "servo")]
517impl Clone for Stylesheet {
518 fn clone(&self) -> Self {
519 let lock = self.shared_lock.clone();
521 let guard = self.shared_lock.read();
522
523 let media = self.media.read_with(&guard).clone();
525 let media = Arc::new(lock.wrap(media));
526 let contents = lock.wrap(Arc::new(
527 self.contents
528 .read_with(&guard)
529 .deep_clone_with_lock(&lock, &guard),
530 ));
531
532 Stylesheet {
533 contents,
534 media,
535 shared_lock: lock,
536 disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
537 }
538 }
539}