1#[cfg(feature = "servo")]
8use crate::animation::DocumentAnimationSet;
9use crate::bloom::StyleBloom;
10use crate::computed_value_flags::ComputedValueFlags;
11use crate::data::{EagerPseudoStyles, ElementData};
12use crate::derives::*;
13use crate::dom::{SendElement, TElement};
14#[cfg(feature = "gecko")]
15use crate::gecko_bindings::structs;
16use crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
17use crate::properties::ComputedValues;
18#[cfg(feature = "servo")]
19use crate::properties::PropertyId;
20use crate::rule_cache::RuleCache;
21use crate::rule_tree::{RuleCascadeFlags, StrongRuleNode};
22use crate::selector_parser::{SnapshotMap, EAGER_PSEUDO_COUNT};
23use crate::shared_lock::StylesheetGuards;
24use crate::sharing::StyleSharingCache;
25use crate::stylist::Stylist;
26use crate::thread_state::{self, ThreadState};
27use crate::traversal::DomTraversal;
28use crate::traversal_flags::TraversalFlags;
29use app_units::Au;
30use euclid::default::Size2D;
31use euclid::Scale;
32#[cfg(feature = "servo")]
33use rustc_hash::FxHashMap;
34use selectors::context::SelectorCaches;
35#[cfg(feature = "gecko")]
36use servo_arc::Arc;
37use std::fmt;
38use std::ops;
39use std::time::{Duration, Instant};
40use style_traits::CSSPixel;
41use style_traits::DevicePixel;
42#[cfg(feature = "servo")]
43use style_traits::SpeculativePainter;
44#[cfg(feature = "servo")]
45use stylo_atoms::Atom;
46
47pub use selectors::matching::QuirksMode;
48
49#[derive(Clone)]
52pub struct StyleSystemOptions {
53 pub disable_style_sharing_cache: bool,
55 pub dump_style_statistics: bool,
57 pub style_statistics_threshold: usize,
60}
61
62#[cfg(feature = "gecko")]
63fn get_env_bool(name: &str) -> bool {
64 use std::env;
65 match env::var(name) {
66 Ok(s) => !s.is_empty(),
67 Err(_) => false,
68 }
69}
70
71const DEFAULT_STATISTICS_THRESHOLD: usize = 50;
72
73#[cfg(feature = "gecko")]
74fn get_env_usize(name: &str) -> Option<usize> {
75 use std::env;
76 env::var(name).ok().map(|s| {
77 s.parse::<usize>()
78 .expect("Couldn't parse environmental variable as usize")
79 })
80}
81
82#[cfg(feature = "servo")]
86pub static DEFAULT_DISABLE_STYLE_SHARING_CACHE: std::sync::atomic::AtomicBool =
87 std::sync::atomic::AtomicBool::new(false);
88
89#[cfg(feature = "servo")]
93pub static DEFAULT_DUMP_STYLE_STATISTICS: std::sync::atomic::AtomicBool =
94 std::sync::atomic::AtomicBool::new(false);
95
96impl Default for StyleSystemOptions {
97 #[cfg(feature = "servo")]
98 fn default() -> Self {
99 use std::sync::atomic::Ordering;
100
101 StyleSystemOptions {
102 disable_style_sharing_cache: DEFAULT_DISABLE_STYLE_SHARING_CACHE
103 .load(Ordering::Relaxed),
104 dump_style_statistics: DEFAULT_DUMP_STYLE_STATISTICS.load(Ordering::Relaxed),
105 style_statistics_threshold: DEFAULT_STATISTICS_THRESHOLD,
106 }
107 }
108
109 #[cfg(feature = "gecko")]
110 fn default() -> Self {
111 StyleSystemOptions {
112 disable_style_sharing_cache: get_env_bool("DISABLE_STYLE_SHARING_CACHE"),
113 dump_style_statistics: get_env_bool("DUMP_STYLE_STATISTICS"),
114 style_statistics_threshold: get_env_usize("STYLE_STATISTICS_THRESHOLD")
115 .unwrap_or(DEFAULT_STATISTICS_THRESHOLD),
116 }
117 }
118}
119
120pub struct SharedStyleContext<'a> {
125 pub stylist: &'a Stylist,
127
128 pub visited_styles_enabled: bool,
133
134 pub options: StyleSystemOptions,
136
137 pub guards: StylesheetGuards<'a>,
139
140 pub current_time_for_animations: f64,
143
144 pub traversal_flags: TraversalFlags,
146
147 pub snapshot_map: &'a SnapshotMap,
149
150 #[cfg(feature = "servo")]
152 pub animations: DocumentAnimationSet,
153
154 #[cfg(feature = "servo")]
156 pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
157}
158
159impl<'a> SharedStyleContext<'a> {
160 pub fn viewport_size(&self) -> Size2D<Au> {
162 self.stylist.device().au_viewport_size()
163 }
164
165 pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
167 self.stylist.device().device_pixel_ratio()
168 }
169
170 pub fn quirks_mode(&self) -> QuirksMode {
172 self.stylist.quirks_mode()
173 }
174}
175
176#[derive(Clone, Debug, Default)]
184pub struct CascadeInputs {
185 pub rules: Option<StrongRuleNode>,
188
189 pub visited_rules: Option<StrongRuleNode>,
194
195 pub flags: ComputedValueFlags,
197
198 pub included_cascade_flags: RuleCascadeFlags,
200}
201
202impl CascadeInputs {
203 pub fn new_from_style(style: &ComputedValues) -> Self {
205 Self {
206 rules: style.rules.clone(),
207 visited_rules: style.visited_style().and_then(|v| v.rules.clone()),
208 flags: style.flags.for_cascade_inputs(),
209 included_cascade_flags: RuleCascadeFlags::empty(),
210 }
211 }
212}
213
214#[derive(Debug)]
217pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
218
219impl Clone for EagerPseudoCascadeInputs {
222 fn clone(&self) -> Self {
223 if self.0.is_none() {
224 return EagerPseudoCascadeInputs(None);
225 }
226 let self_inputs = self.0.as_ref().unwrap();
227 let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
228 for i in 0..EAGER_PSEUDO_COUNT {
229 inputs[i] = self_inputs[i].clone();
230 }
231 EagerPseudoCascadeInputs(Some(inputs))
232 }
233}
234
235impl EagerPseudoCascadeInputs {
236 fn new_from_style(styles: &EagerPseudoStyles) -> Self {
238 EagerPseudoCascadeInputs(styles.as_optional_array().map(|styles| {
239 let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
240 for i in 0..EAGER_PSEUDO_COUNT {
241 inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s));
242 }
243 inputs
244 }))
245 }
246
247 pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
249 self.0
250 }
251}
252
253#[derive(Clone, Debug)]
261pub struct ElementCascadeInputs {
262 pub primary: CascadeInputs,
264 pub pseudos: EagerPseudoCascadeInputs,
266}
267
268impl ElementCascadeInputs {
269 #[inline]
271 pub fn new_from_element_data(data: &ElementData) -> Self {
272 debug_assert!(data.has_styles());
273 ElementCascadeInputs {
274 primary: CascadeInputs::new_from_style(data.styles.primary()),
275 pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos),
276 }
277 }
278}
279
280#[derive(AddAssign, Clone, Default)]
284pub struct PerThreadTraversalStatistics {
285 pub elements_traversed: u32,
287 pub elements_styled: u32,
289 pub elements_matched: u32,
291 pub styles_shared: u32,
293 pub styles_reused: u32,
296}
297
298#[derive(Default)]
301pub struct TraversalStatistics {
302 pub aggregated: PerThreadTraversalStatistics,
304 pub selectors: u32,
306 pub revalidation_selectors: u32,
308 pub dependency_selectors: u32,
310 pub declarations: u32,
312 pub stylist_rebuilds: u32,
314 pub traversal_time: Duration,
316 pub is_parallel: bool,
318 pub is_large: bool,
320}
321
322impl fmt::Display for TraversalStatistics {
325 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
326 writeln!(f, "[PERF] perf block start")?;
327 writeln!(
328 f,
329 "[PERF],traversal,{}",
330 if self.is_parallel {
331 "parallel"
332 } else {
333 "sequential"
334 }
335 )?;
336 writeln!(
337 f,
338 "[PERF],elements_traversed,{}",
339 self.aggregated.elements_traversed
340 )?;
341 writeln!(
342 f,
343 "[PERF],elements_styled,{}",
344 self.aggregated.elements_styled
345 )?;
346 writeln!(
347 f,
348 "[PERF],elements_matched,{}",
349 self.aggregated.elements_matched
350 )?;
351 writeln!(f, "[PERF],styles_shared,{}", self.aggregated.styles_shared)?;
352 writeln!(f, "[PERF],styles_reused,{}", self.aggregated.styles_reused)?;
353 writeln!(f, "[PERF],selectors,{}", self.selectors)?;
354 writeln!(
355 f,
356 "[PERF],revalidation_selectors,{}",
357 self.revalidation_selectors
358 )?;
359 writeln!(
360 f,
361 "[PERF],dependency_selectors,{}",
362 self.dependency_selectors
363 )?;
364 writeln!(f, "[PERF],declarations,{}", self.declarations)?;
365 writeln!(f, "[PERF],stylist_rebuilds,{}", self.stylist_rebuilds)?;
366 writeln!(
367 f,
368 "[PERF],traversal_time_ms,{}",
369 self.traversal_time.as_secs_f64() * 1000.
370 )?;
371 writeln!(f, "[PERF] perf block end")
372 }
373}
374
375impl TraversalStatistics {
376 pub fn new<E, D>(
380 aggregated: PerThreadTraversalStatistics,
381 traversal: &D,
382 parallel: bool,
383 start: Instant,
384 ) -> TraversalStatistics
385 where
386 E: TElement,
387 D: DomTraversal<E>,
388 {
389 let threshold = traversal
390 .shared_context()
391 .options
392 .style_statistics_threshold;
393 let stylist = traversal.shared_context().stylist;
394 let is_large = aggregated.elements_traversed as usize >= threshold;
395 TraversalStatistics {
396 aggregated,
397 selectors: stylist.num_selectors() as u32,
398 revalidation_selectors: stylist.num_revalidation_selectors() as u32,
399 dependency_selectors: stylist.num_invalidations() as u32,
400 declarations: stylist.num_declarations() as u32,
401 stylist_rebuilds: stylist.num_rebuilds() as u32,
402 traversal_time: Instant::now() - start,
403 is_parallel: parallel,
404 is_large,
405 }
406 }
407}
408
409#[cfg(feature = "gecko")]
410bitflags! {
411 pub struct UpdateAnimationsTasks: u8 {
414 const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;
416 const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;
418 const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;
420 const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;
422 const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
427 const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines;
429 const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines;
431 const TIMELINE_SCOPES = structs::UpdateAnimationsTasks_TimelineScopes;
433 }
434}
435
436pub enum SequentialTask<E: TElement> {
440 Unused(SendElement<E>),
442
443 #[cfg(feature = "gecko")]
448 UpdateAnimations {
449 el: SendElement<E>,
451 before_change_style: Option<Arc<ComputedValues>>,
455 tasks: UpdateAnimationsTasks,
457 },
458}
459
460impl<E: TElement> SequentialTask<E> {
461 pub fn execute(self) {
463 use self::SequentialTask::*;
464 debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));
465 match self {
466 Unused(_) => unreachable!(),
467 #[cfg(feature = "gecko")]
468 UpdateAnimations {
469 el,
470 before_change_style,
471 tasks,
472 } => {
473 el.update_animations(before_change_style, tasks);
474 },
475 }
476 }
477
478 #[cfg(feature = "gecko")]
481 pub fn update_animations(
482 el: E,
483 before_change_style: Option<Arc<ComputedValues>>,
484 tasks: UpdateAnimationsTasks,
485 ) -> Self {
486 use self::SequentialTask::*;
487 UpdateAnimations {
488 el: unsafe { SendElement::new(el) },
489 before_change_style,
490 tasks,
491 }
492 }
493}
494
495pub struct SequentialTaskList<E>(Vec<SequentialTask<E>>)
497where
498 E: TElement;
499
500impl<E> ops::Deref for SequentialTaskList<E>
501where
502 E: TElement,
503{
504 type Target = Vec<SequentialTask<E>>;
505
506 fn deref(&self) -> &Self::Target {
507 &self.0
508 }
509}
510
511impl<E> ops::DerefMut for SequentialTaskList<E>
512where
513 E: TElement,
514{
515 fn deref_mut(&mut self) -> &mut Self::Target {
516 &mut self.0
517 }
518}
519
520impl<E> Drop for SequentialTaskList<E>
521where
522 E: TElement,
523{
524 fn drop(&mut self) {
525 debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));
526 for task in self.0.drain(..) {
527 task.execute()
528 }
529 }
530}
531
532pub struct StackLimitChecker {
535 lower_limit: usize,
536}
537
538impl StackLimitChecker {
539 #[inline(never)]
542 pub fn new(stack_size_limit: usize) -> Self {
543 StackLimitChecker {
544 lower_limit: StackLimitChecker::get_sp() - stack_size_limit,
545 }
546 }
547
548 #[inline(never)]
550 pub fn limit_exceeded(&self) -> bool {
551 let curr_sp = StackLimitChecker::get_sp();
552
553 if cfg!(debug_assertions) {
559 let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;
565
566 debug_assert!(stack_bottom < curr_sp);
572
573 let distance_to_stack_bottom = curr_sp - stack_bottom;
579 let max_allowable_distance = (STYLE_THREAD_STACK_SIZE_KB + 10) * 1024;
580 debug_assert!(distance_to_stack_bottom <= max_allowable_distance);
581 }
582
583 curr_sp <= self.lower_limit
585 }
586
587 #[inline(always)]
590 fn get_sp() -> usize {
591 let mut foo: usize = 42;
592 (&mut foo as *mut usize) as usize
593 }
594}
595
596pub struct ThreadLocalStyleContext<E: TElement> {
602 pub sharing_cache: StyleSharingCache<E>,
604 pub rule_cache: RuleCache,
606 pub bloom_filter: StyleBloom<E>,
608 pub tasks: SequentialTaskList<E>,
616 pub statistics: PerThreadTraversalStatistics,
618 pub stack_limit_checker: StackLimitChecker,
621 pub selector_caches: SelectorCaches,
623}
624
625impl<E: TElement> ThreadLocalStyleContext<E> {
626 pub fn new() -> Self {
628 ThreadLocalStyleContext {
629 sharing_cache: StyleSharingCache::new(),
630 rule_cache: RuleCache::new(),
631 bloom_filter: StyleBloom::new(),
632 tasks: SequentialTaskList(Vec::new()),
633 statistics: PerThreadTraversalStatistics::default(),
634 stack_limit_checker: StackLimitChecker::new(
635 (STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,
636 ),
637 selector_caches: SelectorCaches::default(),
638 }
639 }
640}
641
642pub struct StyleContext<'a, E: TElement + 'a> {
645 pub shared: &'a SharedStyleContext<'a>,
647 pub thread_local: &'a mut ThreadLocalStyleContext<E>,
649}
650
651#[cfg(feature = "servo")]
653pub trait RegisteredSpeculativePainter: SpeculativePainter {
654 fn name(&self) -> Atom;
656 fn properties(&self) -> &FxHashMap<Atom, PropertyId>;
658}
659
660#[cfg(feature = "servo")]
662pub trait RegisteredSpeculativePainters: Sync {
663 fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;
665}