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::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
199impl CascadeInputs {
200 pub fn new_from_style(style: &ComputedValues) -> Self {
202 Self {
203 rules: style.rules.clone(),
204 visited_rules: style.visited_style().and_then(|v| v.rules.clone()),
205 flags: style.flags.for_cascade_inputs(),
206 }
207 }
208}
209
210#[derive(Debug)]
213pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
214
215impl Clone for EagerPseudoCascadeInputs {
218 fn clone(&self) -> Self {
219 if self.0.is_none() {
220 return EagerPseudoCascadeInputs(None);
221 }
222 let self_inputs = self.0.as_ref().unwrap();
223 let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
224 for i in 0..EAGER_PSEUDO_COUNT {
225 inputs[i] = self_inputs[i].clone();
226 }
227 EagerPseudoCascadeInputs(Some(inputs))
228 }
229}
230
231impl EagerPseudoCascadeInputs {
232 fn new_from_style(styles: &EagerPseudoStyles) -> Self {
234 EagerPseudoCascadeInputs(styles.as_optional_array().map(|styles| {
235 let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
236 for i in 0..EAGER_PSEUDO_COUNT {
237 inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s));
238 }
239 inputs
240 }))
241 }
242
243 pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
245 self.0
246 }
247}
248
249#[derive(Clone, Debug)]
257pub struct ElementCascadeInputs {
258 pub primary: CascadeInputs,
260 pub pseudos: EagerPseudoCascadeInputs,
262}
263
264impl ElementCascadeInputs {
265 #[inline]
267 pub fn new_from_element_data(data: &ElementData) -> Self {
268 debug_assert!(data.has_styles());
269 ElementCascadeInputs {
270 primary: CascadeInputs::new_from_style(data.styles.primary()),
271 pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos),
272 }
273 }
274}
275
276#[derive(AddAssign, Clone, Default)]
280pub struct PerThreadTraversalStatistics {
281 pub elements_traversed: u32,
283 pub elements_styled: u32,
285 pub elements_matched: u32,
287 pub styles_shared: u32,
289 pub styles_reused: u32,
292}
293
294#[derive(Default)]
297pub struct TraversalStatistics {
298 pub aggregated: PerThreadTraversalStatistics,
300 pub selectors: u32,
302 pub revalidation_selectors: u32,
304 pub dependency_selectors: u32,
306 pub declarations: u32,
308 pub stylist_rebuilds: u32,
310 pub traversal_time: Duration,
312 pub is_parallel: bool,
314 pub is_large: bool,
316}
317
318impl fmt::Display for TraversalStatistics {
321 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
322 writeln!(f, "[PERF] perf block start")?;
323 writeln!(
324 f,
325 "[PERF],traversal,{}",
326 if self.is_parallel {
327 "parallel"
328 } else {
329 "sequential"
330 }
331 )?;
332 writeln!(
333 f,
334 "[PERF],elements_traversed,{}",
335 self.aggregated.elements_traversed
336 )?;
337 writeln!(
338 f,
339 "[PERF],elements_styled,{}",
340 self.aggregated.elements_styled
341 )?;
342 writeln!(
343 f,
344 "[PERF],elements_matched,{}",
345 self.aggregated.elements_matched
346 )?;
347 writeln!(f, "[PERF],styles_shared,{}", self.aggregated.styles_shared)?;
348 writeln!(f, "[PERF],styles_reused,{}", self.aggregated.styles_reused)?;
349 writeln!(f, "[PERF],selectors,{}", self.selectors)?;
350 writeln!(
351 f,
352 "[PERF],revalidation_selectors,{}",
353 self.revalidation_selectors
354 )?;
355 writeln!(
356 f,
357 "[PERF],dependency_selectors,{}",
358 self.dependency_selectors
359 )?;
360 writeln!(f, "[PERF],declarations,{}", self.declarations)?;
361 writeln!(f, "[PERF],stylist_rebuilds,{}", self.stylist_rebuilds)?;
362 writeln!(
363 f,
364 "[PERF],traversal_time_ms,{}",
365 self.traversal_time.as_secs_f64() * 1000.
366 )?;
367 writeln!(f, "[PERF] perf block end")
368 }
369}
370
371impl TraversalStatistics {
372 pub fn new<E, D>(
376 aggregated: PerThreadTraversalStatistics,
377 traversal: &D,
378 parallel: bool,
379 start: Instant,
380 ) -> TraversalStatistics
381 where
382 E: TElement,
383 D: DomTraversal<E>,
384 {
385 let threshold = traversal
386 .shared_context()
387 .options
388 .style_statistics_threshold;
389 let stylist = traversal.shared_context().stylist;
390 let is_large = aggregated.elements_traversed as usize >= threshold;
391 TraversalStatistics {
392 aggregated,
393 selectors: stylist.num_selectors() as u32,
394 revalidation_selectors: stylist.num_revalidation_selectors() as u32,
395 dependency_selectors: stylist.num_invalidations() as u32,
396 declarations: stylist.num_declarations() as u32,
397 stylist_rebuilds: stylist.num_rebuilds() as u32,
398 traversal_time: Instant::now() - start,
399 is_parallel: parallel,
400 is_large,
401 }
402 }
403}
404
405#[cfg(feature = "gecko")]
406bitflags! {
407 pub struct UpdateAnimationsTasks: u8 {
410 const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;
412 const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;
414 const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;
416 const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;
418 const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
423 const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines;
425 const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines;
427 }
428}
429
430pub enum SequentialTask<E: TElement> {
434 Unused(SendElement<E>),
436
437 #[cfg(feature = "gecko")]
442 UpdateAnimations {
443 el: SendElement<E>,
445 before_change_style: Option<Arc<ComputedValues>>,
449 tasks: UpdateAnimationsTasks,
451 },
452}
453
454impl<E: TElement> SequentialTask<E> {
455 pub fn execute(self) {
457 use self::SequentialTask::*;
458 debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));
459 match self {
460 Unused(_) => unreachable!(),
461 #[cfg(feature = "gecko")]
462 UpdateAnimations {
463 el,
464 before_change_style,
465 tasks,
466 } => {
467 el.update_animations(before_change_style, tasks);
468 },
469 }
470 }
471
472 #[cfg(feature = "gecko")]
475 pub fn update_animations(
476 el: E,
477 before_change_style: Option<Arc<ComputedValues>>,
478 tasks: UpdateAnimationsTasks,
479 ) -> Self {
480 use self::SequentialTask::*;
481 UpdateAnimations {
482 el: unsafe { SendElement::new(el) },
483 before_change_style,
484 tasks,
485 }
486 }
487}
488
489pub struct SequentialTaskList<E>(Vec<SequentialTask<E>>)
491where
492 E: TElement;
493
494impl<E> ops::Deref for SequentialTaskList<E>
495where
496 E: TElement,
497{
498 type Target = Vec<SequentialTask<E>>;
499
500 fn deref(&self) -> &Self::Target {
501 &self.0
502 }
503}
504
505impl<E> ops::DerefMut for SequentialTaskList<E>
506where
507 E: TElement,
508{
509 fn deref_mut(&mut self) -> &mut Self::Target {
510 &mut self.0
511 }
512}
513
514impl<E> Drop for SequentialTaskList<E>
515where
516 E: TElement,
517{
518 fn drop(&mut self) {
519 debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));
520 for task in self.0.drain(..) {
521 task.execute()
522 }
523 }
524}
525
526pub struct StackLimitChecker {
529 lower_limit: usize,
530}
531
532impl StackLimitChecker {
533 #[inline(never)]
536 pub fn new(stack_size_limit: usize) -> Self {
537 StackLimitChecker {
538 lower_limit: StackLimitChecker::get_sp() - stack_size_limit,
539 }
540 }
541
542 #[inline(never)]
544 pub fn limit_exceeded(&self) -> bool {
545 let curr_sp = StackLimitChecker::get_sp();
546
547 if cfg!(debug_assertions) {
553 let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;
559
560 debug_assert!(stack_bottom < curr_sp);
566
567 let distance_to_stack_bottom = curr_sp - stack_bottom;
573 let max_allowable_distance = (STYLE_THREAD_STACK_SIZE_KB + 10) * 1024;
574 debug_assert!(distance_to_stack_bottom <= max_allowable_distance);
575 }
576
577 curr_sp <= self.lower_limit
579 }
580
581 #[inline(always)]
584 fn get_sp() -> usize {
585 let mut foo: usize = 42;
586 (&mut foo as *mut usize) as usize
587 }
588}
589
590pub struct ThreadLocalStyleContext<E: TElement> {
596 pub sharing_cache: StyleSharingCache<E>,
598 pub rule_cache: RuleCache,
600 pub bloom_filter: StyleBloom<E>,
602 pub tasks: SequentialTaskList<E>,
610 pub statistics: PerThreadTraversalStatistics,
612 pub stack_limit_checker: StackLimitChecker,
615 pub selector_caches: SelectorCaches,
617}
618
619impl<E: TElement> ThreadLocalStyleContext<E> {
620 pub fn new() -> Self {
622 ThreadLocalStyleContext {
623 sharing_cache: StyleSharingCache::new(),
624 rule_cache: RuleCache::new(),
625 bloom_filter: StyleBloom::new(),
626 tasks: SequentialTaskList(Vec::new()),
627 statistics: PerThreadTraversalStatistics::default(),
628 stack_limit_checker: StackLimitChecker::new(
629 (STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,
630 ),
631 selector_caches: SelectorCaches::default(),
632 }
633 }
634}
635
636pub struct StyleContext<'a, E: TElement + 'a> {
639 pub shared: &'a SharedStyleContext<'a>,
641 pub thread_local: &'a mut ThreadLocalStyleContext<E>,
643}
644
645#[cfg(feature = "servo")]
647pub trait RegisteredSpeculativePainter: SpeculativePainter {
648 fn name(&self) -> Atom;
650 fn properties(&self) -> &FxHashMap<Atom, PropertyId>;
652}
653
654#[cfg(feature = "servo")]
656pub trait RegisteredSpeculativePainters: Sync {
657 fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;
659}