Bug 1756130 [wpt PR 32898] - [CSP] Enhance unsafe-eval test to check both realms...
[gecko.git] / servo / components / style / context.rs
blobb4e574b24fdd6acf3dcf67737658148dcff53e4e
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/. */
5 //! The context within which style is calculated.
7 #[cfg(feature = "servo")]
8 use crate::animation::DocumentAnimationSet;
9 use crate::bloom::StyleBloom;
10 use crate::data::{EagerPseudoStyles, ElementData};
11 use crate::dom::{SendElement, TElement};
12 use crate::font_metrics::FontMetricsProvider;
13 #[cfg(feature = "gecko")]
14 use crate::gecko_bindings::structs;
15 use crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
16 use crate::properties::ComputedValues;
17 #[cfg(feature = "servo")]
18 use crate::properties::PropertyId;
19 use crate::rule_cache::RuleCache;
20 use crate::rule_tree::StrongRuleNode;
21 use crate::selector_parser::{SnapshotMap, EAGER_PSEUDO_COUNT};
22 use crate::shared_lock::StylesheetGuards;
23 use crate::sharing::StyleSharingCache;
24 use crate::stylist::Stylist;
25 use crate::thread_state::{self, ThreadState};
26 use crate::traversal::DomTraversal;
27 use crate::traversal_flags::TraversalFlags;
28 use app_units::Au;
29 use euclid::default::Size2D;
30 use euclid::Scale;
31 use fxhash::FxHashMap;
32 use selectors::matching::ElementSelectorFlags;
33 use selectors::NthIndexCache;
34 #[cfg(feature = "gecko")]
35 use servo_arc::Arc;
36 #[cfg(feature = "servo")]
37 use servo_atoms::Atom;
38 use std::fmt;
39 use std::ops;
40 use style_traits::CSSPixel;
41 use style_traits::DevicePixel;
42 #[cfg(feature = "servo")]
43 use style_traits::SpeculativePainter;
44 use time;
45 use uluru::{Entry, LRUCache};
47 pub use selectors::matching::QuirksMode;
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,
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     }
71 const DEFAULT_STATISTICS_THRESHOLD: usize = 50;
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     })
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);
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);
96 impl Default for StyleSystemOptions {
97     #[cfg(feature = "servo")]
98     fn default() -> Self {
99         use std::sync::atomic::Ordering;
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     }
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     }
120 /// A shared style context.
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,
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,
134     /// Configuration options.
135     pub options: StyleSystemOptions,
137     /// Guards for pre-acquired locks
138     pub guards: StylesheetGuards<'a>,
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,
144     /// Flags controlling how we traverse the tree.
145     pub traversal_flags: TraversalFlags,
147     /// A map with our snapshots in order to handle restyle hints.
148     pub snapshot_map: &'a SnapshotMap,
150     /// The state of all animations for our styled elements.
151     #[cfg(feature = "servo")]
152     pub animations: DocumentAnimationSet,
154     /// Paint worklets
155     #[cfg(feature = "servo")]
156     pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
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     }
165     /// The device pixel ratio
166     pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
167         self.stylist.device().device_pixel_ratio()
168     }
170     /// The quirks mode of the document.
171     pub fn quirks_mode(&self) -> QuirksMode {
172         self.stylist.quirks_mode()
173     }
176 /// The structure holds various intermediate inputs that are eventually used by
177 /// by the cascade.
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>,
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>,
196 impl CascadeInputs {
197     /// Construct inputs from previous cascade results, if any.
198     pub fn new_from_style(style: &ComputedValues) -> Self {
199         CascadeInputs {
200             rules: style.rules.clone(),
201             visited_rules: style.visited_style().and_then(|v| v.rules.clone()),
202         }
203     }
206 /// A list of cascade inputs for eagerly-cascaded pseudo-elements.
207 /// The list is stored inline.
208 #[derive(Debug)]
209 pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
211 // Manually implement `Clone` here because the derived impl of `Clone` for
212 // array types assumes the value inside is `Copy`.
213 impl Clone for EagerPseudoCascadeInputs {
214     fn clone(&self) -> Self {
215         if self.0.is_none() {
216             return EagerPseudoCascadeInputs(None);
217         }
218         let self_inputs = self.0.as_ref().unwrap();
219         let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
220         for i in 0..EAGER_PSEUDO_COUNT {
221             inputs[i] = self_inputs[i].clone();
222         }
223         EagerPseudoCascadeInputs(Some(inputs))
224     }
227 impl EagerPseudoCascadeInputs {
228     /// Construct inputs from previous cascade results, if any.
229     fn new_from_style(styles: &EagerPseudoStyles) -> Self {
230         EagerPseudoCascadeInputs(styles.as_optional_array().map(|styles| {
231             let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
232             for i in 0..EAGER_PSEUDO_COUNT {
233                 inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s));
234             }
235             inputs
236         }))
237     }
239     /// Returns the list of rules, if they exist.
240     pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
241         self.0
242     }
245 /// The cascade inputs associated with a node, including those for any
246 /// pseudo-elements.
248 /// The matching and cascading process stores them in this format temporarily
249 /// within the `CurrentElementInfo`. At the end of the cascade, they are folded
250 /// down into the main `ComputedValues` to reduce memory usage per element while
251 /// still remaining accessible.
252 #[derive(Clone, Debug)]
253 pub struct ElementCascadeInputs {
254     /// The element's cascade inputs.
255     pub primary: CascadeInputs,
256     /// A list of the inputs for the element's eagerly-cascaded pseudo-elements.
257     pub pseudos: EagerPseudoCascadeInputs,
260 impl ElementCascadeInputs {
261     /// Construct inputs from previous cascade results, if any.
262     #[inline]
263     pub fn new_from_element_data(data: &ElementData) -> Self {
264         debug_assert!(data.has_styles());
265         ElementCascadeInputs {
266             primary: CascadeInputs::new_from_style(data.styles.primary()),
267             pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos),
268         }
269     }
272 /// Statistics gathered during the traversal. We gather statistics on each
273 /// thread and then combine them after the threads join via the Add
274 /// implementation below.
275 #[derive(AddAssign, Clone, Default)]
276 pub struct PerThreadTraversalStatistics {
277     /// The total number of elements traversed.
278     pub elements_traversed: u32,
279     /// The number of elements where has_styles() went from false to true.
280     pub elements_styled: u32,
281     /// The number of elements for which we performed selector matching.
282     pub elements_matched: u32,
283     /// The number of cache hits from the StyleSharingCache.
284     pub styles_shared: u32,
285     /// The number of styles reused via rule node comparison from the
286     /// StyleSharingCache.
287     pub styles_reused: u32,
290 /// Statistics gathered during the traversal plus some information from
291 /// other sources including stylist.
292 #[derive(Default)]
293 pub struct TraversalStatistics {
294     /// Aggregated statistics gathered during the traversal.
295     pub aggregated: PerThreadTraversalStatistics,
296     /// The number of selectors in the stylist.
297     pub selectors: u32,
298     /// The number of revalidation selectors.
299     pub revalidation_selectors: u32,
300     /// The number of state/attr dependencies in the dependency set.
301     pub dependency_selectors: u32,
302     /// The number of declarations in the stylist.
303     pub declarations: u32,
304     /// The number of times the stylist was rebuilt.
305     pub stylist_rebuilds: u32,
306     /// Time spent in the traversal, in milliseconds.
307     pub traversal_time_ms: f64,
308     /// Whether this was a parallel traversal.
309     pub is_parallel: bool,
310     /// Whether this is a "large" traversal.
311     pub is_large: bool,
314 /// Format the statistics in a way that the performance test harness understands.
315 /// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331856#c2
316 impl fmt::Display for TraversalStatistics {
317     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
318         debug_assert!(
319             self.traversal_time_ms != 0.0,
320             "should have set traversal time"
321         );
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!(f, "[PERF],traversal_time_ms,{}", self.traversal_time_ms)?;
363         writeln!(f, "[PERF] perf block end")
364     }
367 impl TraversalStatistics {
368     /// Generate complete traversal statistics.
369     ///
370     /// The traversal time is computed given the start time in seconds.
371     pub fn new<E, D>(
372         aggregated: PerThreadTraversalStatistics,
373         traversal: &D,
374         parallel: bool,
375         start: f64,
376     ) -> TraversalStatistics
377     where
378         E: TElement,
379         D: DomTraversal<E>,
380     {
381         let threshold = traversal
382             .shared_context()
383             .options
384             .style_statistics_threshold;
385         let stylist = traversal.shared_context().stylist;
386         let is_large = aggregated.elements_traversed as usize >= threshold;
387         TraversalStatistics {
388             aggregated,
389             selectors: stylist.num_selectors() as u32,
390             revalidation_selectors: stylist.num_revalidation_selectors() as u32,
391             dependency_selectors: stylist.num_invalidations() as u32,
392             declarations: stylist.num_declarations() as u32,
393             stylist_rebuilds: stylist.num_rebuilds() as u32,
394             traversal_time_ms: (time::precise_time_s() - start) * 1000.0,
395             is_parallel: parallel,
396             is_large,
397         }
398     }
401 #[cfg(feature = "gecko")]
402 bitflags! {
403     /// Represents which tasks are performed in a SequentialTask of
404     /// UpdateAnimations which is a result of normal restyle.
405     pub struct UpdateAnimationsTasks: u8 {
406         /// Update CSS Animations.
407         const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;
408         /// Update CSS Transitions.
409         const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;
410         /// Update effect properties.
411         const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;
412         /// Update animation cacade results for animations running on the compositor.
413         const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;
414         /// Display property was changed from none.
415         /// Script animations keep alive on display:none elements, so we need to trigger
416         /// the second animation restyles for the script animations in the case where
417         /// the display property was changed from 'none' to others.
418         const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
419     }
422 #[cfg(feature = "gecko")]
423 bitflags! {
424     /// Represents which tasks are performed in a SequentialTask as a result of
425     /// animation-only restyle.
426     pub struct PostAnimationTasks: u8 {
427         /// Display property was changed from none in animation-only restyle so
428         /// that we need to resolve styles for descendants in a subsequent
429         /// normal restyle.
430         const DISPLAY_CHANGED_FROM_NONE_FOR_SMIL = 0x01;
431     }
434 /// A task to be run in sequential mode on the parent (non-worker) thread. This
435 /// is used by the style system to queue up work which is not safe to do during
436 /// the parallel traversal.
437 pub enum SequentialTask<E: TElement> {
438     /// Entry to avoid an unused type parameter error on servo.
439     Unused(SendElement<E>),
441     /// Performs one of a number of possible tasks related to updating
442     /// animations based on the |tasks| field. These include updating CSS
443     /// animations/transitions that changed as part of the non-animation style
444     /// traversal, and updating the computed effect properties.
445     #[cfg(feature = "gecko")]
446     UpdateAnimations {
447         /// The target element or pseudo-element.
448         el: SendElement<E>,
449         /// The before-change style for transitions. We use before-change style
450         /// as the initial value of its Keyframe. Required if |tasks| includes
451         /// CSSTransitions.
452         before_change_style: Option<Arc<ComputedValues>>,
453         /// The tasks which are performed in this SequentialTask.
454         tasks: UpdateAnimationsTasks,
455     },
457     /// Performs one of a number of possible tasks as a result of animation-only
458     /// restyle.
459     ///
460     /// Currently we do only process for resolving descendant elements that were
461     /// display:none subtree for SMIL animation.
462     #[cfg(feature = "gecko")]
463     PostAnimation {
464         /// The target element.
465         el: SendElement<E>,
466         /// The tasks which are performed in this SequentialTask.
467         tasks: PostAnimationTasks,
468     },
471 impl<E: TElement> SequentialTask<E> {
472     /// Executes this task.
473     pub fn execute(self) {
474         use self::SequentialTask::*;
475         debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
476         match self {
477             Unused(_) => unreachable!(),
478             #[cfg(feature = "gecko")]
479             UpdateAnimations {
480                 el,
481                 before_change_style,
482                 tasks,
483             } => {
484                 el.update_animations(before_change_style, tasks);
485             },
486             #[cfg(feature = "gecko")]
487             PostAnimation { el, tasks } => {
488                 el.process_post_animation(tasks);
489             },
490         }
491     }
493     /// Creates a task to update various animation-related state on a given
494     /// (pseudo-)element.
495     #[cfg(feature = "gecko")]
496     pub fn update_animations(
497         el: E,
498         before_change_style: Option<Arc<ComputedValues>>,
499         tasks: UpdateAnimationsTasks,
500     ) -> Self {
501         use self::SequentialTask::*;
502         UpdateAnimations {
503             el: unsafe { SendElement::new(el) },
504             before_change_style,
505             tasks,
506         }
507     }
509     /// Creates a task to do post-process for a given element as a result of
510     /// animation-only restyle.
511     #[cfg(feature = "gecko")]
512     pub fn process_post_animation(el: E, tasks: PostAnimationTasks) -> Self {
513         use self::SequentialTask::*;
514         PostAnimation {
515             el: unsafe { SendElement::new(el) },
516             tasks,
517         }
518     }
521 type CacheItem<E> = (SendElement<E>, ElementSelectorFlags);
523 /// Map from Elements to ElementSelectorFlags. Used to defer applying selector
524 /// flags until after the traversal.
525 pub struct SelectorFlagsMap<E: TElement> {
526     /// The hashmap storing the flags to apply.
527     map: FxHashMap<SendElement<E>, ElementSelectorFlags>,
528     /// An LRU cache to avoid hashmap lookups, which can be slow if the map
529     /// gets big.
530     cache: LRUCache<[Entry<CacheItem<E>>; 4 + 1]>,
533 #[cfg(debug_assertions)]
534 impl<E: TElement> Drop for SelectorFlagsMap<E> {
535     fn drop(&mut self) {
536         debug_assert!(self.map.is_empty());
537     }
540 impl<E: TElement> SelectorFlagsMap<E> {
541     /// Creates a new empty SelectorFlagsMap.
542     pub fn new() -> Self {
543         SelectorFlagsMap {
544             map: FxHashMap::default(),
545             cache: LRUCache::default(),
546         }
547     }
549     /// Inserts some flags into the map for a given element.
550     pub fn insert_flags(&mut self, element: E, flags: ElementSelectorFlags) {
551         let el = unsafe { SendElement::new(element) };
552         // Check the cache. If the flags have already been noted, we're done.
553         if let Some(item) = self.cache.find(|x| x.0 == el) {
554             if !item.1.contains(flags) {
555                 item.1.insert(flags);
556                 self.map.get_mut(&el).unwrap().insert(flags);
557             }
558             return;
559         }
561         let f = self.map.entry(el).or_insert(ElementSelectorFlags::empty());
562         *f |= flags;
564         self.cache
565             .insert((unsafe { SendElement::new(element) }, *f))
566     }
568     /// Applies the flags. Must be called on the main thread.
569     fn apply_flags(&mut self) {
570         debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
571         self.cache.clear();
572         for (el, flags) in self.map.drain() {
573             unsafe {
574                 el.set_selector_flags(flags);
575             }
576         }
577     }
580 /// A list of SequentialTasks that get executed on Drop.
581 pub struct SequentialTaskList<E>(Vec<SequentialTask<E>>)
582 where
583     E: TElement;
585 impl<E> ops::Deref for SequentialTaskList<E>
586 where
587     E: TElement,
589     type Target = Vec<SequentialTask<E>>;
591     fn deref(&self) -> &Self::Target {
592         &self.0
593     }
596 impl<E> ops::DerefMut for SequentialTaskList<E>
597 where
598     E: TElement,
600     fn deref_mut(&mut self) -> &mut Self::Target {
601         &mut self.0
602     }
605 impl<E> Drop for SequentialTaskList<E>
606 where
607     E: TElement,
609     fn drop(&mut self) {
610         debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
611         for task in self.0.drain(..) {
612             task.execute()
613         }
614     }
617 /// A helper type for stack limit checking.  This assumes that stacks grow
618 /// down, which is true for all non-ancient CPU architectures.
619 pub struct StackLimitChecker {
620     lower_limit: usize,
623 impl StackLimitChecker {
624     /// Create a new limit checker, for this thread, allowing further use
625     /// of up to |stack_size| bytes beyond (below) the current stack pointer.
626     #[inline(never)]
627     pub fn new(stack_size_limit: usize) -> Self {
628         StackLimitChecker {
629             lower_limit: StackLimitChecker::get_sp() - stack_size_limit,
630         }
631     }
633     /// Checks whether the previously stored stack limit has now been exceeded.
634     #[inline(never)]
635     pub fn limit_exceeded(&self) -> bool {
636         let curr_sp = StackLimitChecker::get_sp();
638         // Do some sanity-checking to ensure that our invariants hold, even in
639         // the case where we've exceeded the soft limit.
640         //
641         // The correctness of depends on the assumption that no stack wraps
642         // around the end of the address space.
643         if cfg!(debug_assertions) {
644             // Compute the actual bottom of the stack by subtracting our safety
645             // margin from our soft limit. Note that this will be slightly below
646             // the actual bottom of the stack, because there are a few initial
647             // frames on the stack before we do the measurement that computes
648             // the limit.
649             let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;
651             // The bottom of the stack should be below the current sp. If it
652             // isn't, that means we've either waited too long to check the limit
653             // and burned through our safety margin (in which case we probably
654             // would have segfaulted by now), or we're using a limit computed for
655             // a different thread.
656             debug_assert!(stack_bottom < curr_sp);
658             // Compute the distance between the current sp and the bottom of
659             // the stack, and compare it against the current stack. It should be
660             // no further from us than the total stack size. We allow some slop
661             // to handle the fact that stack_bottom is a bit further than the
662             // bottom of the stack, as discussed above.
663             let distance_to_stack_bottom = curr_sp - stack_bottom;
664             let max_allowable_distance = (STYLE_THREAD_STACK_SIZE_KB + 10) * 1024;
665             debug_assert!(distance_to_stack_bottom <= max_allowable_distance);
666         }
668         // The actual bounds check.
669         curr_sp <= self.lower_limit
670     }
672     // Technically, rustc can optimize this away, but shouldn't for now.
673     // We should fix this once black_box is stable.
674     #[inline(always)]
675     fn get_sp() -> usize {
676         let mut foo: usize = 42;
677         (&mut foo as *mut usize) as usize
678     }
681 /// A thread-local style context.
683 /// This context contains data that needs to be used during restyling, but is
684 /// not required to be unique among worker threads, so we create one per worker
685 /// thread in order to be able to mutate it without locking.
686 pub struct ThreadLocalStyleContext<E: TElement> {
687     /// A cache to share style among siblings.
688     pub sharing_cache: StyleSharingCache<E>,
689     /// A cache from matched properties to elements that match those.
690     pub rule_cache: RuleCache,
691     /// The bloom filter used to fast-reject selector-matching.
692     pub bloom_filter: StyleBloom<E>,
693     /// A set of tasks to be run (on the parent thread) in sequential mode after
694     /// the rest of the styling is complete. This is useful for
695     /// infrequently-needed non-threadsafe operations.
696     ///
697     /// It's important that goes after the style sharing cache and the bloom
698     /// filter, to ensure they're dropped before we execute the tasks, which
699     /// could create another ThreadLocalStyleContext for style computation.
700     pub tasks: SequentialTaskList<E>,
701     /// ElementSelectorFlags that need to be applied after the traversal is
702     /// complete. This map is used in cases where the matching algorithm needs
703     /// to set flags on elements it doesn't have exclusive access to (i.e. other
704     /// than the current element).
705     pub selector_flags: SelectorFlagsMap<E>,
706     /// Statistics about the traversal.
707     pub statistics: PerThreadTraversalStatistics,
708     /// The struct used to compute and cache font metrics from style
709     /// for evaluation of the font-relative em/ch units and font-size
710     pub font_metrics_provider: E::FontMetricsProvider,
711     /// A checker used to ensure that parallel.rs does not recurse indefinitely
712     /// even on arbitrarily deep trees.  See Gecko bug 1376883.
713     pub stack_limit_checker: StackLimitChecker,
714     /// A cache for nth-index-like selectors.
715     pub nth_index_cache: NthIndexCache,
718 impl<E: TElement> ThreadLocalStyleContext<E> {
719     /// Creates a new `ThreadLocalStyleContext` from a shared one.
720     #[cfg(feature = "servo")]
721     pub fn new(shared: &SharedStyleContext) -> Self {
722         ThreadLocalStyleContext {
723             sharing_cache: StyleSharingCache::new(),
724             rule_cache: RuleCache::new(),
725             bloom_filter: StyleBloom::new(),
726             tasks: SequentialTaskList(Vec::new()),
727             selector_flags: SelectorFlagsMap::new(),
728             statistics: PerThreadTraversalStatistics::default(),
729             font_metrics_provider: E::FontMetricsProvider::create_from(shared),
730             stack_limit_checker: StackLimitChecker::new(
731                 (STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,
732             ),
733             nth_index_cache: NthIndexCache::default(),
734         }
735     }
737     #[cfg(feature = "gecko")]
738     /// Creates a new `ThreadLocalStyleContext` from a shared one.
739     pub fn new(shared: &SharedStyleContext) -> Self {
740         ThreadLocalStyleContext {
741             sharing_cache: StyleSharingCache::new(),
742             rule_cache: RuleCache::new(),
743             bloom_filter: StyleBloom::new(),
744             tasks: SequentialTaskList(Vec::new()),
745             selector_flags: SelectorFlagsMap::new(),
746             statistics: PerThreadTraversalStatistics::default(),
747             font_metrics_provider: E::FontMetricsProvider::create_from(shared),
748             stack_limit_checker: StackLimitChecker::new(
749                 (STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,
750             ),
751             nth_index_cache: NthIndexCache::default(),
752         }
753     }
756 impl<E: TElement> Drop for ThreadLocalStyleContext<E> {
757     fn drop(&mut self) {
758         debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
760         // Apply any slow selector flags that need to be set on parents.
761         self.selector_flags.apply_flags();
762     }
765 /// A `StyleContext` is just a simple container for a immutable reference to a
766 /// shared style context, and a mutable reference to a local one.
767 pub struct StyleContext<'a, E: TElement + 'a> {
768     /// The shared style context reference.
769     pub shared: &'a SharedStyleContext<'a>,
770     /// The thread-local style context (mutable) reference.
771     pub thread_local: &'a mut ThreadLocalStyleContext<E>,
774 /// A registered painter
775 #[cfg(feature = "servo")]
776 pub trait RegisteredSpeculativePainter: SpeculativePainter {
777     /// The name it was registered with
778     fn name(&self) -> Atom;
779     /// The properties it was registered with
780     fn properties(&self) -> &FxHashMap<Atom, PropertyId>;
783 /// A set of registered painters
784 #[cfg(feature = "servo")]
785 pub trait RegisteredSpeculativePainters: Sync {
786     /// Look up a speculative painter
787     fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;