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::{ElementContext, 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 crate::values::computed::TreeCountingResult;
30use app_units::Au;
31use euclid::default::Size2D;
32use euclid::Scale;
33use rustc_hash::FxHashMap;
34use selectors::context::SelectorCaches;
35use selectors::OpaqueElement;
36#[cfg(feature = "gecko")]
37use servo_arc::Arc;
38use std::fmt;
39use std::ops;
40use std::time::{Duration, Instant};
41use style_traits::dom::OpaqueNode;
42use style_traits::CSSPixel;
43use style_traits::DevicePixel;
44#[cfg(feature = "servo")]
45use style_traits::SpeculativePainter;
46#[cfg(feature = "servo")]
47use stylo_atoms::Atom;
48
49pub use selectors::matching::QuirksMode;
50
51#[derive(Clone)]
54pub struct StyleSystemOptions {
55 pub disable_style_sharing_cache: bool,
57 pub dump_style_statistics: bool,
59 pub style_statistics_threshold: usize,
62}
63
64#[cfg(feature = "gecko")]
65fn get_env_bool(name: &str) -> bool {
66 use std::env;
67 match env::var(name) {
68 Ok(s) => !s.is_empty(),
69 Err(_) => false,
70 }
71}
72
73const DEFAULT_STATISTICS_THRESHOLD: usize = 50;
74
75#[cfg(feature = "gecko")]
76fn get_env_usize(name: &str) -> Option<usize> {
77 use std::env;
78 env::var(name).ok().map(|s| {
79 s.parse::<usize>()
80 .expect("Couldn't parse environmental variable as usize")
81 })
82}
83
84#[cfg(feature = "servo")]
88pub static DEFAULT_DISABLE_STYLE_SHARING_CACHE: std::sync::atomic::AtomicBool =
89 std::sync::atomic::AtomicBool::new(false);
90
91#[cfg(feature = "servo")]
95pub static DEFAULT_DUMP_STYLE_STATISTICS: std::sync::atomic::AtomicBool =
96 std::sync::atomic::AtomicBool::new(false);
97
98impl Default for StyleSystemOptions {
99 #[cfg(feature = "servo")]
100 fn default() -> Self {
101 use std::sync::atomic::Ordering;
102
103 StyleSystemOptions {
104 disable_style_sharing_cache: DEFAULT_DISABLE_STYLE_SHARING_CACHE
105 .load(Ordering::Relaxed),
106 dump_style_statistics: DEFAULT_DUMP_STYLE_STATISTICS.load(Ordering::Relaxed),
107 style_statistics_threshold: DEFAULT_STATISTICS_THRESHOLD,
108 }
109 }
110
111 #[cfg(feature = "gecko")]
112 fn default() -> Self {
113 StyleSystemOptions {
114 disable_style_sharing_cache: get_env_bool("DISABLE_STYLE_SHARING_CACHE"),
115 dump_style_statistics: get_env_bool("DUMP_STYLE_STATISTICS"),
116 style_statistics_threshold: get_env_usize("STYLE_STATISTICS_THRESHOLD")
117 .unwrap_or(DEFAULT_STATISTICS_THRESHOLD),
118 }
119 }
120}
121
122pub struct SharedStyleContext<'a> {
127 pub stylist: &'a Stylist,
129
130 pub visited_styles_enabled: bool,
135
136 pub options: StyleSystemOptions,
138
139 pub guards: StylesheetGuards<'a>,
141
142 pub current_time_for_animations: f64,
145
146 pub traversal_flags: TraversalFlags,
148
149 pub snapshot_map: &'a SnapshotMap,
151
152 #[cfg(feature = "servo")]
154 pub animations: DocumentAnimationSet,
155
156 #[cfg(feature = "servo")]
158 pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
159}
160
161impl<'a> SharedStyleContext<'a> {
162 pub fn viewport_size(&self) -> Size2D<Au> {
164 self.stylist.device().au_viewport_size()
165 }
166
167 pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
169 self.stylist.device().device_pixel_ratio()
170 }
171
172 pub fn quirks_mode(&self) -> QuirksMode {
174 self.stylist.quirks_mode()
175 }
176}
177
178#[derive(Clone, Debug, Default)]
186pub struct CascadeInputs {
187 pub rules: Option<StrongRuleNode>,
190
191 pub visited_rules: Option<StrongRuleNode>,
196
197 pub flags: ComputedValueFlags,
199
200 pub included_cascade_flags: RuleCascadeFlags,
202}
203
204impl CascadeInputs {
205 pub fn new_from_style(style: &ComputedValues) -> Self {
207 Self {
208 rules: style.rules.clone(),
209 visited_rules: style.visited_style().and_then(|v| v.rules.clone()),
210 flags: style.flags.for_cascade_inputs(),
211 included_cascade_flags: RuleCascadeFlags::empty(),
212 }
213 }
214}
215
216#[derive(Debug)]
219pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
220
221impl Clone for EagerPseudoCascadeInputs {
224 fn clone(&self) -> Self {
225 if self.0.is_none() {
226 return EagerPseudoCascadeInputs(None);
227 }
228 let self_inputs = self.0.as_ref().unwrap();
229 let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
230 for i in 0..EAGER_PSEUDO_COUNT {
231 inputs[i] = self_inputs[i].clone();
232 }
233 EagerPseudoCascadeInputs(Some(inputs))
234 }
235}
236
237impl EagerPseudoCascadeInputs {
238 fn new_from_style(styles: &EagerPseudoStyles) -> Self {
240 EagerPseudoCascadeInputs(styles.as_optional_array().map(|styles| {
241 let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
242 for i in 0..EAGER_PSEUDO_COUNT {
243 inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s));
244 }
245 inputs
246 }))
247 }
248
249 pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
251 self.0
252 }
253}
254
255#[derive(Clone, Debug)]
263pub struct ElementCascadeInputs {
264 pub primary: CascadeInputs,
266 pub pseudos: EagerPseudoCascadeInputs,
268}
269
270impl ElementCascadeInputs {
271 #[inline]
273 pub fn new_from_element_data(data: &ElementData) -> Self {
274 debug_assert!(data.has_styles());
275 ElementCascadeInputs {
276 primary: CascadeInputs::new_from_style(data.styles.primary()),
277 pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos),
278 }
279 }
280}
281
282#[derive(AddAssign, Clone, Default)]
286pub struct PerThreadTraversalStatistics {
287 pub elements_traversed: u32,
289 pub elements_styled: u32,
291 pub elements_matched: u32,
293 pub styles_shared: u32,
295 pub styles_reused: u32,
298}
299
300#[derive(Default)]
303pub struct TraversalStatistics {
304 pub aggregated: PerThreadTraversalStatistics,
306 pub selectors: u32,
308 pub revalidation_selectors: u32,
310 pub dependency_selectors: u32,
312 pub declarations: u32,
314 pub stylist_rebuilds: u32,
316 pub traversal_time: Duration,
318 pub is_parallel: bool,
320 pub is_large: bool,
322}
323
324impl fmt::Display for TraversalStatistics {
327 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
328 writeln!(f, "[PERF] perf block start")?;
329 writeln!(
330 f,
331 "[PERF],traversal,{}",
332 if self.is_parallel {
333 "parallel"
334 } else {
335 "sequential"
336 }
337 )?;
338 writeln!(
339 f,
340 "[PERF],elements_traversed,{}",
341 self.aggregated.elements_traversed
342 )?;
343 writeln!(
344 f,
345 "[PERF],elements_styled,{}",
346 self.aggregated.elements_styled
347 )?;
348 writeln!(
349 f,
350 "[PERF],elements_matched,{}",
351 self.aggregated.elements_matched
352 )?;
353 writeln!(f, "[PERF],styles_shared,{}", self.aggregated.styles_shared)?;
354 writeln!(f, "[PERF],styles_reused,{}", self.aggregated.styles_reused)?;
355 writeln!(f, "[PERF],selectors,{}", self.selectors)?;
356 writeln!(
357 f,
358 "[PERF],revalidation_selectors,{}",
359 self.revalidation_selectors
360 )?;
361 writeln!(
362 f,
363 "[PERF],dependency_selectors,{}",
364 self.dependency_selectors
365 )?;
366 writeln!(f, "[PERF],declarations,{}", self.declarations)?;
367 writeln!(f, "[PERF],stylist_rebuilds,{}", self.stylist_rebuilds)?;
368 writeln!(
369 f,
370 "[PERF],traversal_time_ms,{}",
371 self.traversal_time.as_secs_f64() * 1000.
372 )?;
373 writeln!(f, "[PERF] perf block end")
374 }
375}
376
377impl TraversalStatistics {
378 pub fn new<E, D>(
382 aggregated: PerThreadTraversalStatistics,
383 traversal: &D,
384 parallel: bool,
385 start: Instant,
386 ) -> TraversalStatistics
387 where
388 E: TElement,
389 D: DomTraversal<E>,
390 {
391 let threshold = traversal
392 .shared_context()
393 .options
394 .style_statistics_threshold;
395 let stylist = traversal.shared_context().stylist;
396 let is_large = aggregated.elements_traversed as usize >= threshold;
397 TraversalStatistics {
398 aggregated,
399 selectors: stylist.num_selectors() as u32,
400 revalidation_selectors: stylist.num_revalidation_selectors() as u32,
401 dependency_selectors: stylist.num_invalidations() as u32,
402 declarations: stylist.num_declarations() as u32,
403 stylist_rebuilds: stylist.num_rebuilds() as u32,
404 traversal_time: Instant::now() - start,
405 is_parallel: parallel,
406 is_large,
407 }
408 }
409}
410
411#[cfg(feature = "gecko")]
412bitflags! {
413 pub struct UpdateAnimationsTasks: u8 {
416 const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;
418 const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;
420 const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;
422 const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;
424 const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
429 const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines;
431 const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines;
433 const TIMELINE_SCOPES = structs::UpdateAnimationsTasks_TimelineScopes;
435 }
436}
437
438pub enum SequentialTask<E: TElement> {
442 Unused(SendElement<E>),
444
445 #[cfg(feature = "gecko")]
450 UpdateAnimations {
451 el: SendElement<E>,
453 before_change_style: Option<Arc<ComputedValues>>,
457 tasks: UpdateAnimationsTasks,
459 },
460}
461
462impl<E: TElement> SequentialTask<E> {
463 pub fn execute(self) {
465 use self::SequentialTask::*;
466 debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));
467 match self {
468 Unused(_) => unreachable!(),
469 #[cfg(feature = "gecko")]
470 UpdateAnimations {
471 el,
472 before_change_style,
473 tasks,
474 } => {
475 el.update_animations(before_change_style, tasks);
476 },
477 }
478 }
479
480 #[cfg(feature = "gecko")]
483 pub fn update_animations(
484 el: E,
485 before_change_style: Option<Arc<ComputedValues>>,
486 tasks: UpdateAnimationsTasks,
487 ) -> Self {
488 use self::SequentialTask::*;
489 UpdateAnimations {
490 el: unsafe { SendElement::new(el) },
491 before_change_style,
492 tasks,
493 }
494 }
495}
496
497pub struct SequentialTaskList<E>(Vec<SequentialTask<E>>)
499where
500 E: TElement;
501
502impl<E> ops::Deref for SequentialTaskList<E>
503where
504 E: TElement,
505{
506 type Target = Vec<SequentialTask<E>>;
507
508 fn deref(&self) -> &Self::Target {
509 &self.0
510 }
511}
512
513impl<E> ops::DerefMut for SequentialTaskList<E>
514where
515 E: TElement,
516{
517 fn deref_mut(&mut self) -> &mut Self::Target {
518 &mut self.0
519 }
520}
521
522impl<E> Drop for SequentialTaskList<E>
523where
524 E: TElement,
525{
526 fn drop(&mut self) {
527 debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));
528 for task in self.0.drain(..) {
529 task.execute()
530 }
531 }
532}
533
534pub struct StackLimitChecker {
537 lower_limit: usize,
538}
539
540impl StackLimitChecker {
541 #[inline(never)]
544 pub fn new(stack_size_limit: usize) -> Self {
545 StackLimitChecker {
546 lower_limit: StackLimitChecker::get_sp() - stack_size_limit,
547 }
548 }
549
550 #[inline(never)]
552 pub fn limit_exceeded(&self) -> bool {
553 let curr_sp = StackLimitChecker::get_sp();
554
555 if cfg!(debug_assertions) {
561 let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;
567
568 debug_assert!(stack_bottom < curr_sp);
574
575 let distance_to_stack_bottom = curr_sp - stack_bottom;
581 let max_allowable_distance = (STYLE_THREAD_STACK_SIZE_KB + 10) * 1024;
582 debug_assert!(distance_to_stack_bottom <= max_allowable_distance);
583 }
584
585 curr_sp <= self.lower_limit
587 }
588
589 #[inline(always)]
592 fn get_sp() -> usize {
593 let mut foo: usize = 42;
594 (&mut foo as *mut usize) as usize
595 }
596}
597
598#[derive(Default)]
604pub struct TreeCountingCaches {
605 pub sibling_index: FxHashMap<OpaqueElement, u32>,
607 pub sibling_count: FxHashMap<OpaqueNode, u32>,
609}
610
611impl TreeCountingCaches {
612 pub fn get_or_compute(&mut self, element_context: &dyn ElementContext) -> TreeCountingResult {
615 let (Some(target), Some(parent)) = (
616 element_context.opaque_element(),
617 element_context.opaque_parent(),
618 ) else {
619 return TreeCountingResult::default();
620 };
621
622 let cached_index = self.sibling_index.get(&target).copied();
624 let cached_count = self.sibling_count.get(&parent).copied();
625 if let (Some(index), Some(count)) = (cached_index, cached_count) {
626 return TreeCountingResult::new(index, count);
627 }
628
629 element_context.get_tree_counting_result(self)
632 }
633}
634
635pub struct ThreadLocalStyleContext<E: TElement> {
641 pub sharing_cache: StyleSharingCache<E>,
643 pub rule_cache: RuleCache,
645 pub bloom_filter: StyleBloom<E>,
647 pub tasks: SequentialTaskList<E>,
655 pub statistics: PerThreadTraversalStatistics,
657 pub stack_limit_checker: StackLimitChecker,
660 pub selector_caches: SelectorCaches,
662 pub tree_counting_caches: TreeCountingCaches,
664}
665
666impl<E: TElement> ThreadLocalStyleContext<E> {
667 pub fn new() -> Self {
669 ThreadLocalStyleContext {
670 sharing_cache: StyleSharingCache::new(),
671 rule_cache: RuleCache::new(),
672 bloom_filter: StyleBloom::new(),
673 tasks: SequentialTaskList(Vec::new()),
674 statistics: PerThreadTraversalStatistics::default(),
675 stack_limit_checker: StackLimitChecker::new(
676 (STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,
677 ),
678 selector_caches: SelectorCaches::default(),
679 tree_counting_caches: TreeCountingCaches::default(),
680 }
681 }
682}
683
684pub struct StyleContext<'a, E: TElement + 'a> {
687 pub shared: &'a SharedStyleContext<'a>,
689 pub thread_local: &'a mut ThreadLocalStyleContext<E>,
691}
692
693#[cfg(feature = "servo")]
695pub trait RegisteredSpeculativePainter: SpeculativePainter {
696 fn name(&self) -> Atom;
698 fn properties(&self) -> &FxHashMap<Atom, PropertyId>;
700}
701
702#[cfg(feature = "servo")]
704pub trait RegisteredSpeculativePainters: Sync {
705 fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;
707}