Skip to main content

style/
context.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! The context within which style is calculated.
6
7#[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/// A global options structure for the style system. We use this instead of
52/// opts to abstract across Gecko and Servo.
53#[derive(Clone)]
54pub struct StyleSystemOptions {
55    /// Whether the style sharing cache is disabled.
56    pub disable_style_sharing_cache: bool,
57    /// Whether we should dump statistics about the style system.
58    pub dump_style_statistics: bool,
59    /// The minimum number of elements that must be traversed to trigger a dump
60    /// of style statistics.
61    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/// A global variable holding the state of
85/// `StyleSystemOptions::default().disable_style_sharing_cache`.
86/// See [#22854](https://github.com/servo/servo/issues/22854).
87#[cfg(feature = "servo")]
88pub static DEFAULT_DISABLE_STYLE_SHARING_CACHE: std::sync::atomic::AtomicBool =
89    std::sync::atomic::AtomicBool::new(false);
90
91/// A global variable holding the state of
92/// `StyleSystemOptions::default().dump_style_statistics`.
93/// See [#22854](https://github.com/servo/servo/issues/22854).
94#[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
122/// A shared style context.
123///
124/// There's exactly one of these during a given restyle traversal, and it's
125/// shared among the worker threads.
126pub struct SharedStyleContext<'a> {
127    /// The CSS selector stylist.
128    pub stylist: &'a Stylist,
129
130    /// Whether visited styles are enabled.
131    ///
132    /// They may be disabled when Gecko's pref layout.css.visited_links_enabled
133    /// is false, or when in private browsing mode.
134    pub visited_styles_enabled: bool,
135
136    /// Configuration options.
137    pub options: StyleSystemOptions,
138
139    /// Guards for pre-acquired locks
140    pub guards: StylesheetGuards<'a>,
141
142    /// The current time for transitions and animations. This is needed to ensure
143    /// a consistent sampling time and also to adjust the time for testing.
144    pub current_time_for_animations: f64,
145
146    /// Flags controlling how we traverse the tree.
147    pub traversal_flags: TraversalFlags,
148
149    /// A map with our snapshots in order to handle restyle hints.
150    pub snapshot_map: &'a SnapshotMap,
151
152    /// The state of all animations for our styled elements.
153    #[cfg(feature = "servo")]
154    pub animations: DocumentAnimationSet,
155
156    /// Paint worklets
157    #[cfg(feature = "servo")]
158    pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
159}
160
161impl<'a> SharedStyleContext<'a> {
162    /// Return a suitable viewport size in order to be used for viewport units.
163    pub fn viewport_size(&self) -> Size2D<Au> {
164        self.stylist.device().au_viewport_size()
165    }
166
167    /// The device pixel ratio
168    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
169        self.stylist.device().device_pixel_ratio()
170    }
171
172    /// The quirks mode of the document.
173    pub fn quirks_mode(&self) -> QuirksMode {
174        self.stylist.quirks_mode()
175    }
176}
177
178/// The structure holds various intermediate inputs that are eventually used by
179/// by the cascade.
180///
181/// The matching and cascading process stores them in this format temporarily
182/// within the `CurrentElementInfo`. At the end of the cascade, they are folded
183/// down into the main `ComputedValues` to reduce memory usage per element while
184/// still remaining accessible.
185#[derive(Clone, Debug, Default)]
186pub struct CascadeInputs {
187    /// The rule node representing the ordered list of rules matched for this
188    /// node.
189    pub rules: Option<StrongRuleNode>,
190
191    /// The rule node representing the ordered list of rules matched for this
192    /// node if visited, only computed if there's a relevant link for this
193    /// element. A element's "relevant link" is the element being matched if it
194    /// is a link or the nearest ancestor link.
195    pub visited_rules: Option<StrongRuleNode>,
196
197    /// The set of flags from container queries that we need for invalidation.
198    pub flags: ComputedValueFlags,
199
200    /// The set of RuleCascadeFlags to include in the cascade.
201    pub included_cascade_flags: RuleCascadeFlags,
202}
203
204impl CascadeInputs {
205    /// Construct inputs from previous cascade results, if any.
206    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/// A list of cascade inputs for eagerly-cascaded pseudo-elements.
217/// The list is stored inline.
218#[derive(Debug)]
219pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
220
221// Manually implement `Clone` here because the derived impl of `Clone` for
222// array types assumes the value inside is `Copy`.
223impl 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    /// Construct inputs from previous cascade results, if any.
239    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    /// Returns the list of rules, if they exist.
250    pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
251        self.0
252    }
253}
254
255/// The cascade inputs associated with a node, including those for any
256/// pseudo-elements.
257///
258/// The matching and cascading process stores them in this format temporarily
259/// within the `CurrentElementInfo`. At the end of the cascade, they are folded
260/// down into the main `ComputedValues` to reduce memory usage per element while
261/// still remaining accessible.
262#[derive(Clone, Debug)]
263pub struct ElementCascadeInputs {
264    /// The element's cascade inputs.
265    pub primary: CascadeInputs,
266    /// A list of the inputs for the element's eagerly-cascaded pseudo-elements.
267    pub pseudos: EagerPseudoCascadeInputs,
268}
269
270impl ElementCascadeInputs {
271    /// Construct inputs from previous cascade results, if any.
272    #[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/// Statistics gathered during the traversal. We gather statistics on each
283/// thread and then combine them after the threads join via the Add
284/// implementation below.
285#[derive(AddAssign, Clone, Default)]
286pub struct PerThreadTraversalStatistics {
287    /// The total number of elements traversed.
288    pub elements_traversed: u32,
289    /// The number of elements where has_styles() went from false to true.
290    pub elements_styled: u32,
291    /// The number of elements for which we performed selector matching.
292    pub elements_matched: u32,
293    /// The number of cache hits from the StyleSharingCache.
294    pub styles_shared: u32,
295    /// The number of styles reused via rule node comparison from the
296    /// StyleSharingCache.
297    pub styles_reused: u32,
298}
299
300/// Statistics gathered during the traversal plus some information from
301/// other sources including stylist.
302#[derive(Default)]
303pub struct TraversalStatistics {
304    /// Aggregated statistics gathered during the traversal.
305    pub aggregated: PerThreadTraversalStatistics,
306    /// The number of selectors in the stylist.
307    pub selectors: u32,
308    /// The number of revalidation selectors.
309    pub revalidation_selectors: u32,
310    /// The number of state/attr dependencies in the dependency set.
311    pub dependency_selectors: u32,
312    /// The number of declarations in the stylist.
313    pub declarations: u32,
314    /// The number of times the stylist was rebuilt.
315    pub stylist_rebuilds: u32,
316    /// Time spent in the traversal, in milliseconds.
317    pub traversal_time: Duration,
318    /// Whether this was a parallel traversal.
319    pub is_parallel: bool,
320    /// Whether this is a "large" traversal.
321    pub is_large: bool,
322}
323
324/// Format the statistics in a way that the performance test harness understands.
325/// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331856#c2
326impl 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    /// Generate complete traversal statistics.
379    ///
380    /// The traversal time is computed given the start time in seconds.
381    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    /// Represents which tasks are performed in a SequentialTask of
414    /// UpdateAnimations which is a result of normal restyle.
415    pub struct UpdateAnimationsTasks: u8 {
416        /// Update CSS Animations.
417        const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;
418        /// Update CSS Transitions.
419        const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;
420        /// Update effect properties.
421        const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;
422        /// Update animation cacade results for animations running on the compositor.
423        const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;
424        /// Display property was changed from none.
425        /// Script animations keep alive on display:none elements, so we need to trigger
426        /// the second animation restyles for the script animations in the case where
427        /// the display property was changed from 'none' to others.
428        const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
429        /// Update CSS named scroll progress timelines.
430        const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines;
431        /// Update CSS named view progress timelines.
432        const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines;
433        /// Update CSS timeline scopes, which affect visibility of both scroll and view timelines.
434        const TIMELINE_SCOPES = structs::UpdateAnimationsTasks_TimelineScopes;
435    }
436}
437
438/// A task to be run in sequential mode on the parent (non-worker) thread. This
439/// is used by the style system to queue up work which is not safe to do during
440/// the parallel traversal.
441pub enum SequentialTask<E: TElement> {
442    /// Entry to avoid an unused type parameter error on servo.
443    Unused(SendElement<E>),
444
445    /// Performs one of a number of possible tasks related to updating
446    /// animations based on the |tasks| field. These include updating CSS
447    /// animations/transitions that changed as part of the non-animation style
448    /// traversal, and updating the computed effect properties.
449    #[cfg(feature = "gecko")]
450    UpdateAnimations {
451        /// The target element or pseudo-element.
452        el: SendElement<E>,
453        /// The before-change style for transitions. We use before-change style
454        /// as the initial value of its Keyframe. Required if |tasks| includes
455        /// CSSTransitions.
456        before_change_style: Option<Arc<ComputedValues>>,
457        /// The tasks which are performed in this SequentialTask.
458        tasks: UpdateAnimationsTasks,
459    },
460}
461
462impl<E: TElement> SequentialTask<E> {
463    /// Executes this task.
464    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    /// Creates a task to update various animation-related state on a given
481    /// (pseudo-)element.
482    #[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
497/// A list of SequentialTasks that get executed on Drop.
498pub 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
534/// A helper type for stack limit checking.  This assumes that stacks grow
535/// down, which is true for all non-ancient CPU architectures.
536pub struct StackLimitChecker {
537    lower_limit: usize,
538}
539
540impl StackLimitChecker {
541    /// Create a new limit checker, for this thread, allowing further use
542    /// of up to |stack_size| bytes beyond (below) the current stack pointer.
543    #[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    /// Checks whether the previously stored stack limit has now been exceeded.
551    #[inline(never)]
552    pub fn limit_exceeded(&self) -> bool {
553        let curr_sp = StackLimitChecker::get_sp();
554
555        // Do some sanity-checking to ensure that our invariants hold, even in
556        // the case where we've exceeded the soft limit.
557        //
558        // The correctness of depends on the assumption that no stack wraps
559        // around the end of the address space.
560        if cfg!(debug_assertions) {
561            // Compute the actual bottom of the stack by subtracting our safety
562            // margin from our soft limit. Note that this will be slightly below
563            // the actual bottom of the stack, because there are a few initial
564            // frames on the stack before we do the measurement that computes
565            // the limit.
566            let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;
567
568            // The bottom of the stack should be below the current sp. If it
569            // isn't, that means we've either waited too long to check the limit
570            // and burned through our safety margin (in which case we probably
571            // would have segfaulted by now), or we're using a limit computed for
572            // a different thread.
573            debug_assert!(stack_bottom < curr_sp);
574
575            // Compute the distance between the current sp and the bottom of
576            // the stack, and compare it against the current stack. It should be
577            // no further from us than the total stack size. We allow some slop
578            // to handle the fact that stack_bottom is a bit further than the
579            // bottom of the stack, as discussed above.
580            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        // The actual bounds check.
586        curr_sp <= self.lower_limit
587    }
588
589    // Technically, rustc can optimize this away, but shouldn't for now.
590    // We should fix this once black_box is stable.
591    #[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/// Caches to speed up evalution of tree-counting functions. Separate caches
599/// for index and count are used so that they can be populated in a single
600/// traversal of an element's siblings.
601///
602/// TODO(Bug 2046399) - Consider directly using the SelectorCaches instead.
603#[derive(Default)]
604pub struct TreeCountingCaches {
605    /// A cache of element sibling-index() values.
606    pub sibling_index: FxHashMap<OpaqueElement, u32>,
607    /// A cache of element sibling-count() values, keyed by the element's parent node.
608    pub sibling_count: FxHashMap<OpaqueNode, u32>,
609}
610
611impl TreeCountingCaches {
612    /// Look up the tree-counting function values for the given element. If the element and
613    /// its parent node are not cached, the values are computed and stored.
614    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        // Lookup from the index and count caches
623        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        // Compute the sibling index and sibling count for the element,
630        // inserting into the caches as it traverses through its siblings.
631        element_context.get_tree_counting_result(self)
632    }
633}
634
635/// A thread-local style context.
636///
637/// This context contains data that needs to be used during restyling, but is
638/// not required to be unique among worker threads, so we create one per worker
639/// thread in order to be able to mutate it without locking.
640pub struct ThreadLocalStyleContext<E: TElement> {
641    /// A cache to share style among siblings.
642    pub sharing_cache: StyleSharingCache<E>,
643    /// A cache from matched properties to elements that match those.
644    pub rule_cache: RuleCache,
645    /// The bloom filter used to fast-reject selector-matching.
646    pub bloom_filter: StyleBloom<E>,
647    /// A set of tasks to be run (on the parent thread) in sequential mode after
648    /// the rest of the styling is complete. This is useful for
649    /// infrequently-needed non-threadsafe operations.
650    ///
651    /// It's important that goes after the style sharing cache and the bloom
652    /// filter, to ensure they're dropped before we execute the tasks, which
653    /// could create another ThreadLocalStyleContext for style computation.
654    pub tasks: SequentialTaskList<E>,
655    /// Statistics about the traversal.
656    pub statistics: PerThreadTraversalStatistics,
657    /// A checker used to ensure that parallel.rs does not recurse indefinitely
658    /// even on arbitrarily deep trees.  See Gecko bug 1376883.
659    pub stack_limit_checker: StackLimitChecker,
660    /// Collection of caches (And cache-likes) for speeding up expensive selector matches.
661    pub selector_caches: SelectorCaches,
662    /// Caches for speeding up tree-counting function evaluations.
663    pub tree_counting_caches: TreeCountingCaches,
664}
665
666impl<E: TElement> ThreadLocalStyleContext<E> {
667    /// Creates a new `ThreadLocalStyleContext`
668    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
684/// A `StyleContext` is just a simple container for a immutable reference to a
685/// shared style context, and a mutable reference to a local one.
686pub struct StyleContext<'a, E: TElement + 'a> {
687    /// The shared style context reference.
688    pub shared: &'a SharedStyleContext<'a>,
689    /// The thread-local style context (mutable) reference.
690    pub thread_local: &'a mut ThreadLocalStyleContext<E>,
691}
692
693/// A registered painter
694#[cfg(feature = "servo")]
695pub trait RegisteredSpeculativePainter: SpeculativePainter {
696    /// The name it was registered with
697    fn name(&self) -> Atom;
698    /// The properties it was registered with
699    fn properties(&self) -> &FxHashMap<Atom, PropertyId>;
700}
701
702/// A set of registered painters
703#[cfg(feature = "servo")]
704pub trait RegisteredSpeculativePainters: Sync {
705    /// Look up a speculative painter
706    fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;
707}