tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

context.rs (24447B)


      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")]
      8 use crate::animation::DocumentAnimationSet;
      9 use crate::bloom::StyleBloom;
     10 use crate::computed_value_flags::ComputedValueFlags;
     11 use crate::data::{EagerPseudoStyles, ElementData};
     12 use crate::derives::*;
     13 use crate::dom::{SendElement, TElement};
     14 #[cfg(feature = "gecko")]
     15 use crate::gecko_bindings::structs;
     16 use crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
     17 use crate::properties::ComputedValues;
     18 #[cfg(feature = "servo")]
     19 use crate::properties::PropertyId;
     20 use crate::rule_cache::RuleCache;
     21 use crate::rule_tree::StrongRuleNode;
     22 use crate::selector_parser::{SnapshotMap, EAGER_PSEUDO_COUNT};
     23 use crate::shared_lock::StylesheetGuards;
     24 use crate::sharing::StyleSharingCache;
     25 use crate::stylist::Stylist;
     26 use crate::thread_state::{self, ThreadState};
     27 use crate::traversal::DomTraversal;
     28 use crate::traversal_flags::TraversalFlags;
     29 use app_units::Au;
     30 use euclid::default::Size2D;
     31 use euclid::Scale;
     32 #[cfg(feature = "servo")]
     33 use rustc_hash::FxHashMap;
     34 use selectors::context::SelectorCaches;
     35 #[cfg(feature = "gecko")]
     36 use servo_arc::Arc;
     37 use std::fmt;
     38 use std::ops;
     39 use std::time::{Duration, Instant};
     40 use style_traits::CSSPixel;
     41 use style_traits::DevicePixel;
     42 #[cfg(feature = "servo")]
     43 use style_traits::SpeculativePainter;
     44 #[cfg(feature = "servo")]
     45 use stylo_atoms::Atom;
     46 
     47 pub use selectors::matching::QuirksMode;
     48 
     49 /// A global options structure for the style system. We use this instead of
     50 /// opts to abstract across Gecko and Servo.
     51 #[derive(Clone)]
     52 pub struct StyleSystemOptions {
     53    /// Whether the style sharing cache is disabled.
     54    pub disable_style_sharing_cache: bool,
     55    /// Whether we should dump statistics about the style system.
     56    pub dump_style_statistics: bool,
     57    /// The minimum number of elements that must be traversed to trigger a dump
     58    /// of style statistics.
     59    pub style_statistics_threshold: usize,
     60 }
     61 
     62 #[cfg(feature = "gecko")]
     63 fn 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 
     71 const DEFAULT_STATISTICS_THRESHOLD: usize = 50;
     72 
     73 #[cfg(feature = "gecko")]
     74 fn 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 /// A global variable holding the state of
     83 /// `StyleSystemOptions::default().disable_style_sharing_cache`.
     84 /// See [#22854](https://github.com/servo/servo/issues/22854).
     85 #[cfg(feature = "servo")]
     86 pub static DEFAULT_DISABLE_STYLE_SHARING_CACHE: std::sync::atomic::AtomicBool =
     87    std::sync::atomic::AtomicBool::new(false);
     88 
     89 /// A global variable holding the state of
     90 /// `StyleSystemOptions::default().dump_style_statistics`.
     91 /// See [#22854](https://github.com/servo/servo/issues/22854).
     92 #[cfg(feature = "servo")]
     93 pub static DEFAULT_DUMP_STYLE_STATISTICS: std::sync::atomic::AtomicBool =
     94    std::sync::atomic::AtomicBool::new(false);
     95 
     96 impl 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 
    120 /// A shared style context.
    121 ///
    122 /// There's exactly one of these during a given restyle traversal, and it's
    123 /// shared among the worker threads.
    124 pub struct SharedStyleContext<'a> {
    125    /// The CSS selector stylist.
    126    pub stylist: &'a Stylist,
    127 
    128    /// Whether visited styles are enabled.
    129    ///
    130    /// They may be disabled when Gecko's pref layout.css.visited_links_enabled
    131    /// is false, or when in private browsing mode.
    132    pub visited_styles_enabled: bool,
    133 
    134    /// Configuration options.
    135    pub options: StyleSystemOptions,
    136 
    137    /// Guards for pre-acquired locks
    138    pub guards: StylesheetGuards<'a>,
    139 
    140    /// The current time for transitions and animations. This is needed to ensure
    141    /// a consistent sampling time and also to adjust the time for testing.
    142    pub current_time_for_animations: f64,
    143 
    144    /// Flags controlling how we traverse the tree.
    145    pub traversal_flags: TraversalFlags,
    146 
    147    /// A map with our snapshots in order to handle restyle hints.
    148    pub snapshot_map: &'a SnapshotMap,
    149 
    150    /// The state of all animations for our styled elements.
    151    #[cfg(feature = "servo")]
    152    pub animations: DocumentAnimationSet,
    153 
    154    /// Paint worklets
    155    #[cfg(feature = "servo")]
    156    pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
    157 }
    158 
    159 impl<'a> SharedStyleContext<'a> {
    160    /// Return a suitable viewport size in order to be used for viewport units.
    161    pub fn viewport_size(&self) -> Size2D<Au> {
    162        self.stylist.device().au_viewport_size()
    163    }
    164 
    165    /// The device pixel ratio
    166    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
    167        self.stylist.device().device_pixel_ratio()
    168    }
    169 
    170    /// The quirks mode of the document.
    171    pub fn quirks_mode(&self) -> QuirksMode {
    172        self.stylist.quirks_mode()
    173    }
    174 }
    175 
    176 /// The structure holds various intermediate inputs that are eventually used by
    177 /// by the cascade.
    178 ///
    179 /// The matching and cascading process stores them in this format temporarily
    180 /// within the `CurrentElementInfo`. At the end of the cascade, they are folded
    181 /// down into the main `ComputedValues` to reduce memory usage per element while
    182 /// still remaining accessible.
    183 #[derive(Clone, Debug, Default)]
    184 pub struct CascadeInputs {
    185    /// The rule node representing the ordered list of rules matched for this
    186    /// node.
    187    pub rules: Option<StrongRuleNode>,
    188 
    189    /// The rule node representing the ordered list of rules matched for this
    190    /// node if visited, only computed if there's a relevant link for this
    191    /// element. A element's "relevant link" is the element being matched if it
    192    /// is a link or the nearest ancestor link.
    193    pub visited_rules: Option<StrongRuleNode>,
    194 
    195    /// The set of flags from container queries that we need for invalidation.
    196    pub flags: ComputedValueFlags,
    197 }
    198 
    199 impl CascadeInputs {
    200    /// Construct inputs from previous cascade results, if any.
    201    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 /// A list of cascade inputs for eagerly-cascaded pseudo-elements.
    211 /// The list is stored inline.
    212 #[derive(Debug)]
    213 pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
    214 
    215 // Manually implement `Clone` here because the derived impl of `Clone` for
    216 // array types assumes the value inside is `Copy`.
    217 impl 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 
    231 impl EagerPseudoCascadeInputs {
    232    /// Construct inputs from previous cascade results, if any.
    233    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    /// Returns the list of rules, if they exist.
    244    pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
    245        self.0
    246    }
    247 }
    248 
    249 /// The cascade inputs associated with a node, including those for any
    250 /// pseudo-elements.
    251 ///
    252 /// The matching and cascading process stores them in this format temporarily
    253 /// within the `CurrentElementInfo`. At the end of the cascade, they are folded
    254 /// down into the main `ComputedValues` to reduce memory usage per element while
    255 /// still remaining accessible.
    256 #[derive(Clone, Debug)]
    257 pub struct ElementCascadeInputs {
    258    /// The element's cascade inputs.
    259    pub primary: CascadeInputs,
    260    /// A list of the inputs for the element's eagerly-cascaded pseudo-elements.
    261    pub pseudos: EagerPseudoCascadeInputs,
    262 }
    263 
    264 impl ElementCascadeInputs {
    265    /// Construct inputs from previous cascade results, if any.
    266    #[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 /// Statistics gathered during the traversal. We gather statistics on each
    277 /// thread and then combine them after the threads join via the Add
    278 /// implementation below.
    279 #[derive(AddAssign, Clone, Default)]
    280 pub struct PerThreadTraversalStatistics {
    281    /// The total number of elements traversed.
    282    pub elements_traversed: u32,
    283    /// The number of elements where has_styles() went from false to true.
    284    pub elements_styled: u32,
    285    /// The number of elements for which we performed selector matching.
    286    pub elements_matched: u32,
    287    /// The number of cache hits from the StyleSharingCache.
    288    pub styles_shared: u32,
    289    /// The number of styles reused via rule node comparison from the
    290    /// StyleSharingCache.
    291    pub styles_reused: u32,
    292 }
    293 
    294 /// Statistics gathered during the traversal plus some information from
    295 /// other sources including stylist.
    296 #[derive(Default)]
    297 pub struct TraversalStatistics {
    298    /// Aggregated statistics gathered during the traversal.
    299    pub aggregated: PerThreadTraversalStatistics,
    300    /// The number of selectors in the stylist.
    301    pub selectors: u32,
    302    /// The number of revalidation selectors.
    303    pub revalidation_selectors: u32,
    304    /// The number of state/attr dependencies in the dependency set.
    305    pub dependency_selectors: u32,
    306    /// The number of declarations in the stylist.
    307    pub declarations: u32,
    308    /// The number of times the stylist was rebuilt.
    309    pub stylist_rebuilds: u32,
    310    /// Time spent in the traversal, in milliseconds.
    311    pub traversal_time: Duration,
    312    /// Whether this was a parallel traversal.
    313    pub is_parallel: bool,
    314    /// Whether this is a "large" traversal.
    315    pub is_large: bool,
    316 }
    317 
    318 /// Format the statistics in a way that the performance test harness understands.
    319 /// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331856#c2
    320 impl 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 
    371 impl TraversalStatistics {
    372    /// Generate complete traversal statistics.
    373    ///
    374    /// The traversal time is computed given the start time in seconds.
    375    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")]
    406 bitflags! {
    407    /// Represents which tasks are performed in a SequentialTask of
    408    /// UpdateAnimations which is a result of normal restyle.
    409    pub struct UpdateAnimationsTasks: u8 {
    410        /// Update CSS Animations.
    411        const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;
    412        /// Update CSS Transitions.
    413        const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;
    414        /// Update effect properties.
    415        const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;
    416        /// Update animation cacade results for animations running on the compositor.
    417        const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;
    418        /// Display property was changed from none.
    419        /// Script animations keep alive on display:none elements, so we need to trigger
    420        /// the second animation restyles for the script animations in the case where
    421        /// the display property was changed from 'none' to others.
    422        const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
    423        /// Update CSS named scroll progress timelines.
    424        const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines;
    425        /// Update CSS named view progress timelines.
    426        const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines;
    427    }
    428 }
    429 
    430 /// A task to be run in sequential mode on the parent (non-worker) thread. This
    431 /// is used by the style system to queue up work which is not safe to do during
    432 /// the parallel traversal.
    433 pub enum SequentialTask<E: TElement> {
    434    /// Entry to avoid an unused type parameter error on servo.
    435    Unused(SendElement<E>),
    436 
    437    /// Performs one of a number of possible tasks related to updating
    438    /// animations based on the |tasks| field. These include updating CSS
    439    /// animations/transitions that changed as part of the non-animation style
    440    /// traversal, and updating the computed effect properties.
    441    #[cfg(feature = "gecko")]
    442    UpdateAnimations {
    443        /// The target element or pseudo-element.
    444        el: SendElement<E>,
    445        /// The before-change style for transitions. We use before-change style
    446        /// as the initial value of its Keyframe. Required if |tasks| includes
    447        /// CSSTransitions.
    448        before_change_style: Option<Arc<ComputedValues>>,
    449        /// The tasks which are performed in this SequentialTask.
    450        tasks: UpdateAnimationsTasks,
    451    },
    452 }
    453 
    454 impl<E: TElement> SequentialTask<E> {
    455    /// Executes this task.
    456    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    /// Creates a task to update various animation-related state on a given
    473    /// (pseudo-)element.
    474    #[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 
    489 /// A list of SequentialTasks that get executed on Drop.
    490 pub struct SequentialTaskList<E>(Vec<SequentialTask<E>>)
    491 where
    492    E: TElement;
    493 
    494 impl<E> ops::Deref for SequentialTaskList<E>
    495 where
    496    E: TElement,
    497 {
    498    type Target = Vec<SequentialTask<E>>;
    499 
    500    fn deref(&self) -> &Self::Target {
    501        &self.0
    502    }
    503 }
    504 
    505 impl<E> ops::DerefMut for SequentialTaskList<E>
    506 where
    507    E: TElement,
    508 {
    509    fn deref_mut(&mut self) -> &mut Self::Target {
    510        &mut self.0
    511    }
    512 }
    513 
    514 impl<E> Drop for SequentialTaskList<E>
    515 where
    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 
    526 /// A helper type for stack limit checking.  This assumes that stacks grow
    527 /// down, which is true for all non-ancient CPU architectures.
    528 pub struct StackLimitChecker {
    529    lower_limit: usize,
    530 }
    531 
    532 impl StackLimitChecker {
    533    /// Create a new limit checker, for this thread, allowing further use
    534    /// of up to |stack_size| bytes beyond (below) the current stack pointer.
    535    #[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    /// Checks whether the previously stored stack limit has now been exceeded.
    543    #[inline(never)]
    544    pub fn limit_exceeded(&self) -> bool {
    545        let curr_sp = StackLimitChecker::get_sp();
    546 
    547        // Do some sanity-checking to ensure that our invariants hold, even in
    548        // the case where we've exceeded the soft limit.
    549        //
    550        // The correctness of depends on the assumption that no stack wraps
    551        // around the end of the address space.
    552        if cfg!(debug_assertions) {
    553            // Compute the actual bottom of the stack by subtracting our safety
    554            // margin from our soft limit. Note that this will be slightly below
    555            // the actual bottom of the stack, because there are a few initial
    556            // frames on the stack before we do the measurement that computes
    557            // the limit.
    558            let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;
    559 
    560            // The bottom of the stack should be below the current sp. If it
    561            // isn't, that means we've either waited too long to check the limit
    562            // and burned through our safety margin (in which case we probably
    563            // would have segfaulted by now), or we're using a limit computed for
    564            // a different thread.
    565            debug_assert!(stack_bottom < curr_sp);
    566 
    567            // Compute the distance between the current sp and the bottom of
    568            // the stack, and compare it against the current stack. It should be
    569            // no further from us than the total stack size. We allow some slop
    570            // to handle the fact that stack_bottom is a bit further than the
    571            // bottom of the stack, as discussed above.
    572            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        // The actual bounds check.
    578        curr_sp <= self.lower_limit
    579    }
    580 
    581    // Technically, rustc can optimize this away, but shouldn't for now.
    582    // We should fix this once black_box is stable.
    583    #[inline(always)]
    584    fn get_sp() -> usize {
    585        let mut foo: usize = 42;
    586        (&mut foo as *mut usize) as usize
    587    }
    588 }
    589 
    590 /// A thread-local style context.
    591 ///
    592 /// This context contains data that needs to be used during restyling, but is
    593 /// not required to be unique among worker threads, so we create one per worker
    594 /// thread in order to be able to mutate it without locking.
    595 pub struct ThreadLocalStyleContext<E: TElement> {
    596    /// A cache to share style among siblings.
    597    pub sharing_cache: StyleSharingCache<E>,
    598    /// A cache from matched properties to elements that match those.
    599    pub rule_cache: RuleCache,
    600    /// The bloom filter used to fast-reject selector-matching.
    601    pub bloom_filter: StyleBloom<E>,
    602    /// A set of tasks to be run (on the parent thread) in sequential mode after
    603    /// the rest of the styling is complete. This is useful for
    604    /// infrequently-needed non-threadsafe operations.
    605    ///
    606    /// It's important that goes after the style sharing cache and the bloom
    607    /// filter, to ensure they're dropped before we execute the tasks, which
    608    /// could create another ThreadLocalStyleContext for style computation.
    609    pub tasks: SequentialTaskList<E>,
    610    /// Statistics about the traversal.
    611    pub statistics: PerThreadTraversalStatistics,
    612    /// A checker used to ensure that parallel.rs does not recurse indefinitely
    613    /// even on arbitrarily deep trees.  See Gecko bug 1376883.
    614    pub stack_limit_checker: StackLimitChecker,
    615    /// Collection of caches (And cache-likes) for speeding up expensive selector matches.
    616    pub selector_caches: SelectorCaches,
    617 }
    618 
    619 impl<E: TElement> ThreadLocalStyleContext<E> {
    620    /// Creates a new `ThreadLocalStyleContext`
    621    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 
    636 /// A `StyleContext` is just a simple container for a immutable reference to a
    637 /// shared style context, and a mutable reference to a local one.
    638 pub struct StyleContext<'a, E: TElement + 'a> {
    639    /// The shared style context reference.
    640    pub shared: &'a SharedStyleContext<'a>,
    641    /// The thread-local style context (mutable) reference.
    642    pub thread_local: &'a mut ThreadLocalStyleContext<E>,
    643 }
    644 
    645 /// A registered painter
    646 #[cfg(feature = "servo")]
    647 pub trait RegisteredSpeculativePainter: SpeculativePainter {
    648    /// The name it was registered with
    649    fn name(&self) -> Atom;
    650    /// The properties it was registered with
    651    fn properties(&self) -> &FxHashMap<Atom, PropertyId>;
    652 }
    653 
    654 /// A set of registered painters
    655 #[cfg(feature = "servo")]
    656 pub trait RegisteredSpeculativePainters: Sync {
    657    /// Look up a speculative painter
    658    fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;
    659 }