Source code

Revision control

Other Tools

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::Animation;
9
use crate::bloom::StyleBloom;
10
use crate::data::{EagerPseudoStyles, ElementData};
11
#[cfg(feature = "servo")]
12
use crate::dom::OpaqueNode;
13
use crate::dom::{SendElement, TElement};
14
use crate::font_metrics::FontMetricsProvider;
15
#[cfg(feature = "gecko")]
16
use crate::gecko_bindings::structs;
17
use crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
18
use crate::properties::ComputedValues;
19
#[cfg(feature = "servo")]
20
use crate::properties::PropertyId;
21
use crate::rule_cache::RuleCache;
22
use crate::rule_tree::StrongRuleNode;
23
use crate::selector_parser::{SnapshotMap, EAGER_PSEUDO_COUNT};
24
use crate::shared_lock::StylesheetGuards;
25
use crate::sharing::StyleSharingCache;
26
use crate::stylist::Stylist;
27
use crate::thread_state::{self, ThreadState};
28
use crate::timer::Timer;
29
use crate::traversal::DomTraversal;
30
use crate::traversal_flags::TraversalFlags;
31
use app_units::Au;
32
#[cfg(feature = "servo")]
33
use crossbeam_channel::Sender;
34
use euclid::default::Size2D;
35
use euclid::Scale;
36
use fxhash::FxHashMap;
37
#[cfg(feature = "servo")]
38
use parking_lot::RwLock;
39
use selectors::matching::ElementSelectorFlags;
40
use selectors::NthIndexCache;
41
use servo_arc::Arc;
42
#[cfg(feature = "servo")]
43
use servo_atoms::Atom;
44
use std::fmt;
45
use std::ops;
46
#[cfg(feature = "servo")]
47
use std::sync::Mutex;
48
use style_traits::CSSPixel;
49
use style_traits::DevicePixel;
50
#[cfg(feature = "servo")]
51
use style_traits::SpeculativePainter;
52
use time;
53
use uluru::{Entry, LRUCache};
54
55
pub use selectors::matching::QuirksMode;
56
57
/// This structure is used to create a local style context from a shared one.
58
#[cfg(feature = "servo")]
59
pub struct ThreadLocalStyleContextCreationInfo {
60
new_animations_sender: Sender<Animation>,
61
}
62
63
#[cfg(feature = "servo")]
64
impl ThreadLocalStyleContextCreationInfo {
65
/// Trivially constructs a `ThreadLocalStyleContextCreationInfo`.
66
pub fn new(animations_sender: Sender<Animation>) -> Self {
67
ThreadLocalStyleContextCreationInfo {
68
new_animations_sender: animations_sender,
69
}
70
}
71
}
72
73
/// A global options structure for the style system. We use this instead of
74
/// opts to abstract across Gecko and Servo.
75
#[derive(Clone)]
76
pub struct StyleSystemOptions {
77
/// Whether the style sharing cache is disabled.
78
pub disable_style_sharing_cache: bool,
79
/// Whether we should dump statistics about the style system.
80
pub dump_style_statistics: bool,
81
/// The minimum number of elements that must be traversed to trigger a dump
82
/// of style statistics.
83
pub style_statistics_threshold: usize,
84
}
85
86
#[cfg(feature = "gecko")]
87
fn get_env_bool(name: &str) -> bool {
88
use std::env;
89
match env::var(name) {
90
Ok(s) => !s.is_empty(),
91
Err(_) => false,
92
}
93
}
94
95
const DEFAULT_STATISTICS_THRESHOLD: usize = 50;
96
97
#[cfg(feature = "gecko")]
98
fn get_env_usize(name: &str) -> Option<usize> {
99
use std::env;
100
env::var(name).ok().map(|s| {
101
s.parse::<usize>()
102
.expect("Couldn't parse environmental variable as usize")
103
})
104
}
105
106
/// A global variable holding the state of
107
/// `StyleSystemOptions::default().disable_style_sharing_cache`.
109
#[cfg(feature = "servo")]
110
pub static DEFAULT_DISABLE_STYLE_SHARING_CACHE: std::sync::atomic::AtomicBool =
111
std::sync::atomic::AtomicBool::new(false);
112
113
/// A global variable holding the state of
114
/// `StyleSystemOptions::default().dump_style_statistics`.
116
#[cfg(feature = "servo")]
117
pub static DEFAULT_DUMP_STYLE_STATISTICS: std::sync::atomic::AtomicBool =
118
std::sync::atomic::AtomicBool::new(false);
119
120
impl Default for StyleSystemOptions {
121
#[cfg(feature = "servo")]
122
fn default() -> Self {
123
use std::sync::atomic::Ordering;
124
125
StyleSystemOptions {
126
disable_style_sharing_cache: DEFAULT_DISABLE_STYLE_SHARING_CACHE
127
.load(Ordering::Relaxed),
128
dump_style_statistics: DEFAULT_DUMP_STYLE_STATISTICS.load(Ordering::Relaxed),
129
style_statistics_threshold: DEFAULT_STATISTICS_THRESHOLD,
130
}
131
}
132
133
#[cfg(feature = "gecko")]
134
fn default() -> Self {
135
StyleSystemOptions {
136
disable_style_sharing_cache: get_env_bool("DISABLE_STYLE_SHARING_CACHE"),
137
dump_style_statistics: get_env_bool("DUMP_STYLE_STATISTICS"),
138
style_statistics_threshold: get_env_usize("STYLE_STATISTICS_THRESHOLD")
139
.unwrap_or(DEFAULT_STATISTICS_THRESHOLD),
140
}
141
}
142
}
143
144
impl StyleSystemOptions {
145
#[cfg(feature = "servo")]
146
/// On Gecko's nightly build?
147
pub fn is_nightly(&self) -> bool {
148
false
149
}
150
151
#[cfg(feature = "gecko")]
152
/// On Gecko's nightly build?
153
#[inline]
154
pub fn is_nightly(&self) -> bool {
155
structs::GECKO_IS_NIGHTLY
156
}
157
}
158
159
/// A shared style context.
160
///
161
/// There's exactly one of these during a given restyle traversal, and it's
162
/// shared among the worker threads.
163
pub struct SharedStyleContext<'a> {
164
/// The CSS selector stylist.
165
pub stylist: &'a Stylist,
166
167
/// Whether visited styles are enabled.
168
///
169
/// They may be disabled when Gecko's pref layout.css.visited_links_enabled
170
/// is false, or when in private browsing mode.
171
pub visited_styles_enabled: bool,
172
173
/// Configuration options.
174
pub options: StyleSystemOptions,
175
176
/// Guards for pre-acquired locks
177
pub guards: StylesheetGuards<'a>,
178
179
/// The current timer for transitions and animations. This is needed to test
180
/// them.
181
pub timer: Timer,
182
183
/// Flags controlling how we traverse the tree.
184
pub traversal_flags: TraversalFlags,
185
186
/// A map with our snapshots in order to handle restyle hints.
187
pub snapshot_map: &'a SnapshotMap,
188
189
/// The animations that are currently running.
190
#[cfg(feature = "servo")]
191
pub running_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
192
193
/// The list of animations that have expired since the last style recalculation.
194
#[cfg(feature = "servo")]
195
pub expired_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
196
197
/// Paint worklets
198
#[cfg(feature = "servo")]
199
pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
200
201
/// Data needed to create the thread-local style context from the shared one.
202
#[cfg(feature = "servo")]
203
pub local_context_creation_data: Mutex<ThreadLocalStyleContextCreationInfo>,
204
}
205
206
impl<'a> SharedStyleContext<'a> {
207
/// Return a suitable viewport size in order to be used for viewport units.
208
pub fn viewport_size(&self) -> Size2D<Au> {
209
self.stylist.device().au_viewport_size()
210
}
211
212
/// The device pixel ratio
213
pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
214
self.stylist.device().device_pixel_ratio()
215
}
216
217
/// The quirks mode of the document.
218
pub fn quirks_mode(&self) -> QuirksMode {
219
self.stylist.quirks_mode()
220
}
221
}
222
223
/// The structure holds various intermediate inputs that are eventually used by
224
/// by the cascade.
225
///
226
/// The matching and cascading process stores them in this format temporarily
227
/// within the `CurrentElementInfo`. At the end of the cascade, they are folded
228
/// down into the main `ComputedValues` to reduce memory usage per element while
229
/// still remaining accessible.
230
#[derive(Clone, Debug, Default)]
231
pub struct CascadeInputs {
232
/// The rule node representing the ordered list of rules matched for this
233
/// node.
234
pub rules: Option<StrongRuleNode>,
235
236
/// The rule node representing the ordered list of rules matched for this
237
/// node if visited, only computed if there's a relevant link for this
238
/// element. A element's "relevant link" is the element being matched if it
239
/// is a link or the nearest ancestor link.
240
pub visited_rules: Option<StrongRuleNode>,
241
}
242
243
impl CascadeInputs {
244
/// Construct inputs from previous cascade results, if any.
245
pub fn new_from_style(style: &ComputedValues) -> Self {
246
CascadeInputs {
247
rules: style.rules.clone(),
248
visited_rules: style.visited_style().and_then(|v| v.rules.clone()),
249
}
250
}
251
}
252
253
/// A list of cascade inputs for eagerly-cascaded pseudo-elements.
254
/// The list is stored inline.
255
#[derive(Debug)]
256
pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
257
258
// Manually implement `Clone` here because the derived impl of `Clone` for
259
// array types assumes the value inside is `Copy`.
260
impl Clone for EagerPseudoCascadeInputs {
261
fn clone(&self) -> Self {
262
if self.0.is_none() {
263
return EagerPseudoCascadeInputs(None);
264
}
265
let self_inputs = self.0.as_ref().unwrap();
266
let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
267
for i in 0..EAGER_PSEUDO_COUNT {
268
inputs[i] = self_inputs[i].clone();
269
}
270
EagerPseudoCascadeInputs(Some(inputs))
271
}
272
}
273
274
impl EagerPseudoCascadeInputs {
275
/// Construct inputs from previous cascade results, if any.
276
fn new_from_style(styles: &EagerPseudoStyles) -> Self {
277
EagerPseudoCascadeInputs(styles.as_optional_array().map(|styles| {
278
let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
279
for i in 0..EAGER_PSEUDO_COUNT {
280
inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s));
281
}
282
inputs
283
}))
284
}
285
286
/// Returns the list of rules, if they exist.
287
pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
288
self.0
289
}
290
}
291
292
/// The cascade inputs associated with a node, including those for any
293
/// pseudo-elements.
294
///
295
/// The matching and cascading process stores them in this format temporarily
296
/// within the `CurrentElementInfo`. At the end of the cascade, they are folded
297
/// down into the main `ComputedValues` to reduce memory usage per element while
298
/// still remaining accessible.
299
#[derive(Clone, Debug)]
300
pub struct ElementCascadeInputs {
301
/// The element's cascade inputs.
302
pub primary: CascadeInputs,
303
/// A list of the inputs for the element's eagerly-cascaded pseudo-elements.
304
pub pseudos: EagerPseudoCascadeInputs,
305
}
306
307
impl ElementCascadeInputs {
308
/// Construct inputs from previous cascade results, if any.
309
#[inline]
310
pub fn new_from_element_data(data: &ElementData) -> Self {
311
debug_assert!(data.has_styles());
312
ElementCascadeInputs {
313
primary: CascadeInputs::new_from_style(data.styles.primary()),
314
pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos),
315
}
316
}
317
}
318
319
/// Statistics gathered during the traversal. We gather statistics on each
320
/// thread and then combine them after the threads join via the Add
321
/// implementation below.
322
#[derive(AddAssign, Clone, Default)]
323
pub struct PerThreadTraversalStatistics {
324
/// The total number of elements traversed.
325
pub elements_traversed: u32,
326
/// The number of elements where has_styles() went from false to true.
327
pub elements_styled: u32,
328
/// The number of elements for which we performed selector matching.
329
pub elements_matched: u32,
330
/// The number of cache hits from the StyleSharingCache.
331
pub styles_shared: u32,
332
/// The number of styles reused via rule node comparison from the
333
/// StyleSharingCache.
334
pub styles_reused: u32,
335
}
336
337
/// Statistics gathered during the traversal plus some information from
338
/// other sources including stylist.
339
#[derive(Default)]
340
pub struct TraversalStatistics {
341
/// Aggregated statistics gathered during the traversal.
342
pub aggregated: PerThreadTraversalStatistics,
343
/// The number of selectors in the stylist.
344
pub selectors: u32,
345
/// The number of revalidation selectors.
346
pub revalidation_selectors: u32,
347
/// The number of state/attr dependencies in the dependency set.
348
pub dependency_selectors: u32,
349
/// The number of declarations in the stylist.
350
pub declarations: u32,
351
/// The number of times the stylist was rebuilt.
352
pub stylist_rebuilds: u32,
353
/// Time spent in the traversal, in milliseconds.
354
pub traversal_time_ms: f64,
355
/// Whether this was a parallel traversal.
356
pub is_parallel: bool,
357
/// Whether this is a "large" traversal.
358
pub is_large: bool,
359
}
360
361
/// Format the statistics in a way that the performance test harness understands.
363
impl fmt::Display for TraversalStatistics {
364
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
365
debug_assert!(
366
self.traversal_time_ms != 0.0,
367
"should have set traversal time"
368
);
369
writeln!(f, "[PERF] perf block start")?;
370
writeln!(
371
f,
372
"[PERF],traversal,{}",
373
if self.is_parallel {
374
"parallel"
375
} else {
376
"sequential"
377
}
378
)?;
379
writeln!(
380
f,
381
"[PERF],elements_traversed,{}",
382
self.aggregated.elements_traversed
383
)?;
384
writeln!(
385
f,
386
"[PERF],elements_styled,{}",
387
self.aggregated.elements_styled
388
)?;
389
writeln!(
390
f,
391
"[PERF],elements_matched,{}",
392
self.aggregated.elements_matched
393
)?;
394
writeln!(f, "[PERF],styles_shared,{}", self.aggregated.styles_shared)?;
395
writeln!(f, "[PERF],styles_reused,{}", self.aggregated.styles_reused)?;
396
writeln!(f, "[PERF],selectors,{}", self.selectors)?;
397
writeln!(
398
f,
399
"[PERF],revalidation_selectors,{}",
400
self.revalidation_selectors
401
)?;
402
writeln!(
403
f,
404
"[PERF],dependency_selectors,{}",
405
self.dependency_selectors
406
)?;
407
writeln!(f, "[PERF],declarations,{}", self.declarations)?;
408
writeln!(f, "[PERF],stylist_rebuilds,{}", self.stylist_rebuilds)?;
409
writeln!(f, "[PERF],traversal_time_ms,{}", self.traversal_time_ms)?;
410
writeln!(f, "[PERF] perf block end")
411
}
412
}
413
414
impl TraversalStatistics {
415
/// Generate complete traversal statistics.
416
///
417
/// The traversal time is computed given the start time in seconds.
418
pub fn new<E, D>(
419
aggregated: PerThreadTraversalStatistics,
420
traversal: &D,
421
parallel: bool,
422
start: f64,
423
) -> TraversalStatistics
424
where
425
E: TElement,
426
D: DomTraversal<E>,
427
{
428
let threshold = traversal
429
.shared_context()
430
.options
431
.style_statistics_threshold;
432
let stylist = traversal.shared_context().stylist;
433
let is_large = aggregated.elements_traversed as usize >= threshold;
434
TraversalStatistics {
435
aggregated,
436
selectors: stylist.num_selectors() as u32,
437
revalidation_selectors: stylist.num_revalidation_selectors() as u32,
438
dependency_selectors: stylist.num_invalidations() as u32,
439
declarations: stylist.num_declarations() as u32,
440
stylist_rebuilds: stylist.num_rebuilds() as u32,
441
traversal_time_ms: (time::precise_time_s() - start) * 1000.0,
442
is_parallel: parallel,
443
is_large,
444
}
445
}
446
}
447
448
#[cfg(feature = "gecko")]
449
bitflags! {
450
/// Represents which tasks are performed in a SequentialTask of
451
/// UpdateAnimations which is a result of normal restyle.
452
pub struct UpdateAnimationsTasks: u8 {
453
/// Update CSS Animations.
454
const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;
455
/// Update CSS Transitions.
456
const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;
457
/// Update effect properties.
458
const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;
459
/// Update animation cacade results for animations running on the compositor.
460
const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;
461
/// Display property was changed from none.
462
/// Script animations keep alive on display:none elements, so we need to trigger
463
/// the second animation restyles for the script animations in the case where
464
/// the display property was changed from 'none' to others.
465
const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
466
}
467
}
468
469
#[cfg(feature = "gecko")]
470
bitflags! {
471
/// Represents which tasks are performed in a SequentialTask as a result of
472
/// animation-only restyle.
473
pub struct PostAnimationTasks: u8 {
474
/// Display property was changed from none in animation-only restyle so
475
/// that we need to resolve styles for descendants in a subsequent
476
/// normal restyle.
477
const DISPLAY_CHANGED_FROM_NONE_FOR_SMIL = 0x01;
478
}
479
}
480
481
/// A task to be run in sequential mode on the parent (non-worker) thread. This
482
/// is used by the style system to queue up work which is not safe to do during
483
/// the parallel traversal.
484
pub enum SequentialTask<E: TElement> {
485
/// Entry to avoid an unused type parameter error on servo.
486
Unused(SendElement<E>),
487
488
/// Performs one of a number of possible tasks related to updating
489
/// animations based on the |tasks| field. These include updating CSS
490
/// animations/transitions that changed as part of the non-animation style
491
/// traversal, and updating the computed effect properties.
492
#[cfg(feature = "gecko")]
493
UpdateAnimations {
494
/// The target element or pseudo-element.
495
el: SendElement<E>,
496
/// The before-change style for transitions. We use before-change style
497
/// as the initial value of its Keyframe. Required if |tasks| includes
498
/// CSSTransitions.
499
before_change_style: Option<Arc<ComputedValues>>,
500
/// The tasks which are performed in this SequentialTask.
501
tasks: UpdateAnimationsTasks,
502
},
503
504
/// Performs one of a number of possible tasks as a result of animation-only
505
/// restyle.
506
///
507
/// Currently we do only process for resolving descendant elements that were
508
/// display:none subtree for SMIL animation.
509
#[cfg(feature = "gecko")]
510
PostAnimation {
511
/// The target element.
512
el: SendElement<E>,
513
/// The tasks which are performed in this SequentialTask.
514
tasks: PostAnimationTasks,
515
},
516
}
517
518
impl<E: TElement> SequentialTask<E> {
519
/// Executes this task.
520
pub fn execute(self) {
521
use self::SequentialTask::*;
522
debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
523
match self {
524
Unused(_) => unreachable!(),
525
#[cfg(feature = "gecko")]
526
UpdateAnimations {
527
el,
528
before_change_style,
529
tasks,
530
} => {
531
el.update_animations(before_change_style, tasks);
532
},
533
#[cfg(feature = "gecko")]
534
PostAnimation { el, tasks } => {
535
el.process_post_animation(tasks);
536
},
537
}
538
}
539
540
/// Creates a task to update various animation-related state on a given
541
/// (pseudo-)element.
542
#[cfg(feature = "gecko")]
543
pub fn update_animations(
544
el: E,
545
before_change_style: Option<Arc<ComputedValues>>,
546
tasks: UpdateAnimationsTasks,
547
) -> Self {
548
use self::SequentialTask::*;
549
UpdateAnimations {
550
el: unsafe { SendElement::new(el) },
551
before_change_style,
552
tasks,
553
}
554
}
555
556
/// Creates a task to do post-process for a given element as a result of
557
/// animation-only restyle.
558
#[cfg(feature = "gecko")]
559
pub fn process_post_animation(el: E, tasks: PostAnimationTasks) -> Self {
560
use self::SequentialTask::*;
561
PostAnimation {
562
el: unsafe { SendElement::new(el) },
563
tasks,
564
}
565
}
566
}
567
568
type CacheItem<E> = (SendElement<E>, ElementSelectorFlags);
569
570
/// Map from Elements to ElementSelectorFlags. Used to defer applying selector
571
/// flags until after the traversal.
572
pub struct SelectorFlagsMap<E: TElement> {
573
/// The hashmap storing the flags to apply.
574
map: FxHashMap<SendElement<E>, ElementSelectorFlags>,
575
/// An LRU cache to avoid hashmap lookups, which can be slow if the map
576
/// gets big.
577
cache: LRUCache<[Entry<CacheItem<E>>; 4 + 1]>,
578
}
579
580
#[cfg(debug_assertions)]
581
impl<E: TElement> Drop for SelectorFlagsMap<E> {
582
fn drop(&mut self) {
583
debug_assert!(self.map.is_empty());
584
}
585
}
586
587
impl<E: TElement> SelectorFlagsMap<E> {
588
/// Creates a new empty SelectorFlagsMap.
589
pub fn new() -> Self {
590
SelectorFlagsMap {
591
map: FxHashMap::default(),
592
cache: LRUCache::default(),
593
}
594
}
595
596
/// Inserts some flags into the map for a given element.
597
pub fn insert_flags(&mut self, element: E, flags: ElementSelectorFlags) {
598
let el = unsafe { SendElement::new(element) };
599
// Check the cache. If the flags have already been noted, we're done.
600
if let Some(item) = self.cache.find(|x| x.0 == el) {
601
if !item.1.contains(flags) {
602
item.1.insert(flags);
603
self.map.get_mut(&el).unwrap().insert(flags);
604
}
605
return;
606
}
607
608
let f = self.map.entry(el).or_insert(ElementSelectorFlags::empty());
609
*f |= flags;
610
611
self.cache
612
.insert((unsafe { SendElement::new(element) }, *f))
613
}
614
615
/// Applies the flags. Must be called on the main thread.
616
fn apply_flags(&mut self) {
617
debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
618
self.cache.evict_all();
619
for (el, flags) in self.map.drain() {
620
unsafe {
621
el.set_selector_flags(flags);
622
}
623
}
624
}
625
}
626
627
/// A list of SequentialTasks that get executed on Drop.
628
pub struct SequentialTaskList<E>(Vec<SequentialTask<E>>)
629
where
630
E: TElement;
631
632
impl<E> ops::Deref for SequentialTaskList<E>
633
where
634
E: TElement,
635
{
636
type Target = Vec<SequentialTask<E>>;
637
638
fn deref(&self) -> &Self::Target {
639
&self.0
640
}
641
}
642
643
impl<E> ops::DerefMut for SequentialTaskList<E>
644
where
645
E: TElement,
646
{
647
fn deref_mut(&mut self) -> &mut Self::Target {
648
&mut self.0
649
}
650
}
651
652
impl<E> Drop for SequentialTaskList<E>
653
where
654
E: TElement,
655
{
656
fn drop(&mut self) {
657
debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
658
for task in self.0.drain(..) {
659
task.execute()
660
}
661
}
662
}
663
664
/// A helper type for stack limit checking. This assumes that stacks grow
665
/// down, which is true for all non-ancient CPU architectures.
666
pub struct StackLimitChecker {
667
lower_limit: usize,
668
}
669
670
impl StackLimitChecker {
671
/// Create a new limit checker, for this thread, allowing further use
672
/// of up to |stack_size| bytes beyond (below) the current stack pointer.
673
#[inline(never)]
674
pub fn new(stack_size_limit: usize) -> Self {
675
StackLimitChecker {
676
lower_limit: StackLimitChecker::get_sp() - stack_size_limit,
677
}
678
}
679
680
/// Checks whether the previously stored stack limit has now been exceeded.
681
#[inline(never)]
682
pub fn limit_exceeded(&self) -> bool {
683
let curr_sp = StackLimitChecker::get_sp();
684
685
// Do some sanity-checking to ensure that our invariants hold, even in
686
// the case where we've exceeded the soft limit.
687
//
688
// The correctness of depends on the assumption that no stack wraps
689
// around the end of the address space.
690
if cfg!(debug_assertions) {
691
// Compute the actual bottom of the stack by subtracting our safety
692
// margin from our soft limit. Note that this will be slightly below
693
// the actual bottom of the stack, because there are a few initial
694
// frames on the stack before we do the measurement that computes
695
// the limit.
696
let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;
697
698
// The bottom of the stack should be below the current sp. If it
699
// isn't, that means we've either waited too long to check the limit
700
// and burned through our safety margin (in which case we probably
701
// would have segfaulted by now), or we're using a limit computed for
702
// a different thread.
703
debug_assert!(stack_bottom < curr_sp);
704
705
// Compute the distance between the current sp and the bottom of
706
// the stack, and compare it against the current stack. It should be
707
// no further from us than the total stack size. We allow some slop
708
// to handle the fact that stack_bottom is a bit further than the
709
// bottom of the stack, as discussed above.
710
let distance_to_stack_bottom = curr_sp - stack_bottom;
711
let max_allowable_distance = (STYLE_THREAD_STACK_SIZE_KB + 10) * 1024;
712
debug_assert!(distance_to_stack_bottom <= max_allowable_distance);
713
}
714
715
// The actual bounds check.
716
curr_sp <= self.lower_limit
717
}
718
719
// Technically, rustc can optimize this away, but shouldn't for now.
720
// We should fix this once black_box is stable.
721
#[inline(always)]
722
fn get_sp() -> usize {
723
let mut foo: usize = 42;
724
(&mut foo as *mut usize) as usize
725
}
726
}
727
728
/// A thread-local style context.
729
///
730
/// This context contains data that needs to be used during restyling, but is
731
/// not required to be unique among worker threads, so we create one per worker
732
/// thread in order to be able to mutate it without locking.
733
pub struct ThreadLocalStyleContext<E: TElement> {
734
/// A cache to share style among siblings.
735
pub sharing_cache: StyleSharingCache<E>,
736
/// A cache from matched properties to elements that match those.
737
pub rule_cache: RuleCache,
738
/// The bloom filter used to fast-reject selector-matching.
739
pub bloom_filter: StyleBloom<E>,
740
/// A channel on which new animations that have been triggered by style
741
/// recalculation can be sent.
742
#[cfg(feature = "servo")]
743
pub new_animations_sender: Sender<Animation>,
744
/// A set of tasks to be run (on the parent thread) in sequential mode after
745
/// the rest of the styling is complete. This is useful for
746
/// infrequently-needed non-threadsafe operations.
747
///
748
/// It's important that goes after the style sharing cache and the bloom
749
/// filter, to ensure they're dropped before we execute the tasks, which
750
/// could create another ThreadLocalStyleContext for style computation.
751
pub tasks: SequentialTaskList<E>,
752
/// ElementSelectorFlags that need to be applied after the traversal is
753
/// complete. This map is used in cases where the matching algorithm needs
754
/// to set flags on elements it doesn't have exclusive access to (i.e. other
755
/// than the current element).
756
pub selector_flags: SelectorFlagsMap<E>,
757
/// Statistics about the traversal.
758
pub statistics: PerThreadTraversalStatistics,
759
/// The struct used to compute and cache font metrics from style
760
/// for evaluation of the font-relative em/ch units and font-size
761
pub font_metrics_provider: E::FontMetricsProvider,
762
/// A checker used to ensure that parallel.rs does not recurse indefinitely
763
/// even on arbitrarily deep trees. See Gecko bug 1376883.
764
pub stack_limit_checker: StackLimitChecker,
765
/// A cache for nth-index-like selectors.
766
pub nth_index_cache: NthIndexCache,
767
}
768
769
impl<E: TElement> ThreadLocalStyleContext<E> {
770
/// Creates a new `ThreadLocalStyleContext` from a shared one.
771
#[cfg(feature = "servo")]
772
pub fn new(shared: &SharedStyleContext) -> Self {
773
ThreadLocalStyleContext {
774
sharing_cache: StyleSharingCache::new(),
775
rule_cache: RuleCache::new(),
776
bloom_filter: StyleBloom::new(),
777
new_animations_sender: shared
778
.local_context_creation_data
779
.lock()
780
.unwrap()
781
.new_animations_sender
782
.clone(),
783
tasks: SequentialTaskList(Vec::new()),
784
selector_flags: SelectorFlagsMap::new(),
785
statistics: PerThreadTraversalStatistics::default(),
786
font_metrics_provider: E::FontMetricsProvider::create_from(shared),
787
stack_limit_checker: StackLimitChecker::new(
788
(STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,
789
),
790
nth_index_cache: NthIndexCache::default(),
791
}
792
}
793
794
#[cfg(feature = "gecko")]
795
/// Creates a new `ThreadLocalStyleContext` from a shared one.
796
pub fn new(shared: &SharedStyleContext) -> Self {
797
ThreadLocalStyleContext {
798
sharing_cache: StyleSharingCache::new(),
799
rule_cache: RuleCache::new(),
800
bloom_filter: StyleBloom::new(),
801
tasks: SequentialTaskList(Vec::new()),
802
selector_flags: SelectorFlagsMap::new(),
803
statistics: PerThreadTraversalStatistics::default(),
804
font_metrics_provider: E::FontMetricsProvider::create_from(shared),
805
stack_limit_checker: StackLimitChecker::new(
806
(STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,
807
),
808
nth_index_cache: NthIndexCache::default(),
809
}
810
}
811
}
812
813
impl<E: TElement> Drop for ThreadLocalStyleContext<E> {
814
fn drop(&mut self) {
815
debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
816
817
// Apply any slow selector flags that need to be set on parents.
818
self.selector_flags.apply_flags();
819
}
820
}
821
822
/// A `StyleContext` is just a simple container for a immutable reference to a
823
/// shared style context, and a mutable reference to a local one.
824
pub struct StyleContext<'a, E: TElement + 'a> {
825
/// The shared style context reference.
826
pub shared: &'a SharedStyleContext<'a>,
827
/// The thread-local style context (mutable) reference.
828
pub thread_local: &'a mut ThreadLocalStyleContext<E>,
829
}
830
831
/// A registered painter
832
#[cfg(feature = "servo")]
833
pub trait RegisteredSpeculativePainter: SpeculativePainter {
834
/// The name it was registered with
835
fn name(&self) -> Atom;
836
/// The properties it was registered with
837
fn properties(&self) -> &FxHashMap<Atom, PropertyId>;
838
}
839
840
/// A set of registered painters
841
#[cfg(feature = "servo")]
842
pub trait RegisteredSpeculativePainters: Sync {
843
/// Look up a speculative painter
844
fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;
845
}