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
//! Selector matching.
6
7
use crate::applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
8
use crate::context::{CascadeInputs, QuirksMode};
9
use crate::dom::{TElement, TShadowRoot};
10
use crate::element_state::{DocumentState, ElementState};
11
use crate::font_metrics::FontMetricsProvider;
12
#[cfg(feature = "gecko")]
13
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
14
use crate::invalidation::element::invalidation_map::InvalidationMap;
15
use crate::invalidation::media_queries::{EffectiveMediaQueryResults, ToMediaListKey};
16
use crate::media_queries::Device;
17
use crate::properties::{self, CascadeMode, ComputedValues};
18
use crate::properties::{AnimationRules, PropertyDeclarationBlock};
19
use crate::rule_cache::{RuleCache, RuleCacheConditions};
20
use crate::rule_collector::{containing_shadow_ignoring_svg_use, RuleCollector};
21
use crate::rule_tree::{CascadeLevel, RuleTree, ShadowCascadeOrder, StrongRuleNode, StyleSource};
22
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry};
23
use crate::selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap};
24
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
25
use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKind};
26
use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
27
use crate::stylesheets::keyframes_rule::KeyframesAnimation;
28
use crate::stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
29
use crate::stylesheets::StyleRule;
30
use crate::stylesheets::StylesheetInDocument;
31
#[cfg(feature = "gecko")]
32
use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule};
33
use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
34
use crate::thread_state::{self, ThreadState};
35
use crate::{Atom, LocalName, Namespace, WeakAtom};
36
use fallible::FallibleVec;
37
use hashglobe::FailedAllocationError;
38
use malloc_size_of::MallocSizeOf;
39
#[cfg(feature = "gecko")]
40
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
41
use selectors::attr::{CaseSensitivity, NamespaceConstraint};
42
use selectors::bloom::BloomFilter;
43
use selectors::matching::VisitedHandlingMode;
44
use selectors::matching::{matches_selector, ElementSelectorFlags, MatchingContext, MatchingMode};
45
use selectors::parser::{AncestorHashes, Combinator, Component, Selector};
46
use selectors::parser::{SelectorIter, Visit};
47
use selectors::visitor::SelectorVisitor;
48
use selectors::NthIndexCache;
49
use servo_arc::{Arc, ArcBorrow};
50
use smallbitvec::SmallBitVec;
51
use smallvec::SmallVec;
52
use std::sync::Mutex;
53
use std::{mem, ops};
54
use style_traits::viewport::ViewportConstraints;
55
56
/// The type of the stylesheets that the stylist contains.
57
#[cfg(feature = "servo")]
58
pub type StylistSheet = crate::stylesheets::DocumentStyleSheet;
59
60
/// The type of the stylesheets that the stylist contains.
61
#[cfg(feature = "gecko")]
62
pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;
63
64
lazy_static! {
65
/// A cache of computed user-agent data, to be shared across documents.
66
static ref UA_CASCADE_DATA_CACHE: Mutex<UserAgentCascadeDataCache> =
67
Mutex::new(UserAgentCascadeDataCache::new());
68
}
69
70
struct UserAgentCascadeDataCache {
71
entries: Vec<Arc<UserAgentCascadeData>>,
72
}
73
74
impl UserAgentCascadeDataCache {
75
fn new() -> Self {
76
Self { entries: vec![] }
77
}
78
79
fn len(&self) -> usize {
80
self.entries.len()
81
}
82
83
// FIXME(emilio): This may need to be keyed on quirks-mode too, though there
84
// aren't class / id selectors on those sheets, usually, so it's probably
85
// ok...
86
fn lookup<'a, I, S>(
87
&'a mut self,
88
sheets: I,
89
device: &Device,
90
quirks_mode: QuirksMode,
91
guard: &SharedRwLockReadGuard,
92
) -> Result<Arc<UserAgentCascadeData>, FailedAllocationError>
93
where
94
I: Iterator<Item = &'a S> + Clone,
95
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
96
{
97
let mut key = EffectiveMediaQueryResults::new();
98
debug!("UserAgentCascadeDataCache::lookup({:?})", device);
99
for sheet in sheets.clone() {
100
CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key)
101
}
102
103
for entry in &self.entries {
104
if entry.cascade_data.effective_media_query_results == key {
105
return Ok(entry.clone());
106
}
107
}
108
109
let mut new_data = UserAgentCascadeData {
110
cascade_data: CascadeData::new(),
111
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
112
};
113
114
debug!("> Picking the slow path");
115
116
for sheet in sheets {
117
new_data.cascade_data.add_stylesheet(
118
device,
119
quirks_mode,
120
sheet,
121
guard,
122
SheetRebuildKind::Full,
123
Some(&mut new_data.precomputed_pseudo_element_decls),
124
)?;
125
}
126
127
let new_data = Arc::new(new_data);
128
self.entries.push(new_data.clone());
129
Ok(new_data)
130
}
131
132
/// Returns all the cascade datas that are not being used (that is, that are
133
/// held alive just by this cache).
134
///
135
/// We return them instead of dropping in place because some of them may
136
/// keep alive some other documents (like the SVG documents kept alive by
137
/// URL references), and thus we don't want to drop them while locking the
138
/// cache to not deadlock.
139
fn take_unused(&mut self) -> SmallVec<[Arc<UserAgentCascadeData>; 3]> {
140
let mut unused = SmallVec::new();
141
for i in (0..self.entries.len()).rev() {
142
// is_unique() returns false for static references, but we never
143
// have static references to UserAgentCascadeDatas. If we did, it
144
// may not make sense to put them in the cache in the first place.
145
if self.entries[i].is_unique() {
146
unused.push(self.entries.remove(i));
147
}
148
}
149
unused
150
}
151
152
fn take_all(&mut self) -> Vec<Arc<UserAgentCascadeData>> {
153
mem::replace(&mut self.entries, Vec::new())
154
}
155
156
#[cfg(feature = "gecko")]
157
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
158
sizes.mOther += self.entries.shallow_size_of(ops);
159
for arc in self.entries.iter() {
160
// These are primary Arc references that can be measured
161
// unconditionally.
162
sizes.mOther += arc.unconditional_shallow_size_of(ops);
163
arc.add_size_of(ops, sizes);
164
}
165
}
166
}
167
168
/// Measure heap usage of UA_CASCADE_DATA_CACHE.
169
#[cfg(feature = "gecko")]
170
pub fn add_size_of_ua_cache(ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
171
UA_CASCADE_DATA_CACHE
172
.lock()
173
.unwrap()
174
.add_size_of(ops, sizes);
175
}
176
177
type PrecomputedPseudoElementDeclarations = PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>;
178
179
#[derive(Default)]
180
struct UserAgentCascadeData {
181
cascade_data: CascadeData,
182
183
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
184
///
185
/// These are eagerly computed once, and then used to resolve the new
186
/// computed values on the fly on layout.
187
///
188
/// These are only filled from UA stylesheets.
189
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations,
190
}
191
192
impl UserAgentCascadeData {
193
#[cfg(feature = "gecko")]
194
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
195
self.cascade_data.add_size_of(ops, sizes);
196
sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops);
197
}
198
}
199
200
/// All the computed information for all the stylesheets that apply to the
201
/// document.
202
#[derive(Default)]
203
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
204
pub struct DocumentCascadeData {
205
#[cfg_attr(
206
feature = "servo",
207
ignore_malloc_size_of = "Arc, owned by UserAgentCascadeDataCache"
208
)]
209
user_agent: Arc<UserAgentCascadeData>,
210
user: CascadeData,
211
author: CascadeData,
212
per_origin: PerOrigin<()>,
213
}
214
215
/// An iterator over the cascade data of a given document.
216
pub struct DocumentCascadeDataIter<'a> {
217
iter: PerOriginIter<'a, ()>,
218
cascade_data: &'a DocumentCascadeData,
219
}
220
221
impl<'a> Iterator for DocumentCascadeDataIter<'a> {
222
type Item = (&'a CascadeData, Origin);
223
224
fn next(&mut self) -> Option<Self::Item> {
225
let (_, origin) = self.iter.next()?;
226
Some((self.cascade_data.borrow_for_origin(origin), origin))
227
}
228
}
229
230
impl DocumentCascadeData {
231
/// Borrows the cascade data for a given origin.
232
#[inline]
233
pub fn borrow_for_origin(&self, origin: Origin) -> &CascadeData {
234
match origin {
235
Origin::UserAgent => &self.user_agent.cascade_data,
236
Origin::Author => &self.author,
237
Origin::User => &self.user,
238
}
239
}
240
241
fn iter_origins(&self) -> DocumentCascadeDataIter {
242
DocumentCascadeDataIter {
243
iter: self.per_origin.iter_origins(),
244
cascade_data: self,
245
}
246
}
247
248
fn iter_origins_rev(&self) -> DocumentCascadeDataIter {
249
DocumentCascadeDataIter {
250
iter: self.per_origin.iter_origins_rev(),
251
cascade_data: self,
252
}
253
}
254
255
/// Rebuild the cascade data for the given document stylesheets, and
256
/// optionally with a set of user agent stylesheets. Returns Err(..)
257
/// to signify OOM.
258
fn rebuild<'a, S>(
259
&mut self,
260
device: &Device,
261
quirks_mode: QuirksMode,
262
mut flusher: DocumentStylesheetFlusher<'a, S>,
263
guards: &StylesheetGuards,
264
) -> Result<(), FailedAllocationError>
265
where
266
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
267
{
268
// First do UA sheets.
269
{
270
if flusher.flush_origin(Origin::UserAgent).dirty() {
271
let origin_sheets = flusher.origin_sheets(Origin::UserAgent);
272
let _unused_cascade_datas = {
273
let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
274
self.user_agent =
275
ua_cache.lookup(origin_sheets, device, quirks_mode, guards.ua_or_user)?;
276
debug!("User agent data cache size {:?}", ua_cache.len());
277
ua_cache.take_unused()
278
};
279
}
280
}
281
282
// Now do the user sheets.
283
self.user.rebuild(
284
device,
285
quirks_mode,
286
flusher.flush_origin(Origin::User),
287
guards.ua_or_user,
288
)?;
289
290
// And now the author sheets.
291
self.author.rebuild(
292
device,
293
quirks_mode,
294
flusher.flush_origin(Origin::Author),
295
guards.author,
296
)?;
297
298
Ok(())
299
}
300
301
/// Measures heap usage.
302
#[cfg(feature = "gecko")]
303
pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
304
self.user.add_size_of(ops, sizes);
305
self.author.add_size_of(ops, sizes);
306
}
307
}
308
309
/// Whether author styles are enabled.
310
///
311
/// This is used to support Gecko.
312
#[allow(missing_docs)]
313
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
314
pub enum AuthorStylesEnabled {
315
Yes,
316
No,
317
}
318
319
/// A wrapper over a DocumentStylesheetSet that can be `Sync`, since it's only
320
/// used and exposed via mutable methods in the `Stylist`.
321
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
322
struct StylistStylesheetSet(DocumentStylesheetSet<StylistSheet>);
323
// Read above to see why this is fine.
324
unsafe impl Sync for StylistStylesheetSet {}
325
326
impl StylistStylesheetSet {
327
fn new() -> Self {
328
StylistStylesheetSet(DocumentStylesheetSet::new())
329
}
330
}
331
332
impl ops::Deref for StylistStylesheetSet {
333
type Target = DocumentStylesheetSet<StylistSheet>;
334
335
fn deref(&self) -> &Self::Target {
336
&self.0
337
}
338
}
339
340
impl ops::DerefMut for StylistStylesheetSet {
341
fn deref_mut(&mut self) -> &mut Self::Target {
342
&mut self.0
343
}
344
}
345
346
/// This structure holds all the selectors and device characteristics
347
/// for a given document. The selectors are converted into `Rule`s
348
/// and sorted into `SelectorMap`s keyed off stylesheet origin and
349
/// pseudo-element (see `CascadeData`).
350
///
351
/// This structure is effectively created once per pipeline, in the
352
/// LayoutThread corresponding to that pipeline.
353
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
354
pub struct Stylist {
355
/// Device that the stylist is currently evaluating against.
356
///
357
/// This field deserves a bigger comment due to the different use that Gecko
358
/// and Servo give to it (that we should eventually unify).
359
///
360
/// With Gecko, the device is never changed. Gecko manually tracks whether
361
/// the device data should be reconstructed, and "resets" the state of the
362
/// device.
363
///
364
/// On Servo, on the other hand, the device is a really cheap representation
365
/// that is recreated each time some constraint changes and calling
366
/// `set_device`.
367
device: Device,
368
369
/// Viewport constraints based on the current device.
370
viewport_constraints: Option<ViewportConstraints>,
371
372
/// The list of stylesheets.
373
stylesheets: StylistStylesheetSet,
374
375
/// If true, the quirks-mode stylesheet is applied.
376
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")]
377
quirks_mode: QuirksMode,
378
379
/// Selector maps for all of the style sheets in the stylist, after
380
/// evalutaing media rules against the current device, split out per
381
/// cascade level.
382
cascade_data: DocumentCascadeData,
383
384
/// Whether author styles are enabled.
385
author_styles_enabled: AuthorStylesEnabled,
386
387
/// The rule tree, that stores the results of selector matching.
388
rule_tree: RuleTree,
389
390
/// The total number of times the stylist has been rebuilt.
391
num_rebuilds: usize,
392
}
393
394
/// What cascade levels to include when styling elements.
395
#[derive(Clone, Copy, PartialEq)]
396
pub enum RuleInclusion {
397
/// Include rules for style sheets at all cascade levels. This is the
398
/// normal rule inclusion mode.
399
All,
400
/// Only include rules from UA and user level sheets. Used to implement
401
/// `getDefaultComputedStyle`.
402
DefaultOnly,
403
}
404
405
#[cfg(feature = "gecko")]
406
impl From<StyleRuleInclusion> for RuleInclusion {
407
fn from(value: StyleRuleInclusion) -> Self {
408
match value {
409
StyleRuleInclusion::All => RuleInclusion::All,
410
StyleRuleInclusion::DefaultOnly => RuleInclusion::DefaultOnly,
411
}
412
}
413
}
414
415
impl Stylist {
416
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
417
/// If more members are added here, think about whether they should
418
/// be reset in clear().
419
#[inline]
420
pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
421
Self {
422
viewport_constraints: None,
423
device,
424
quirks_mode,
425
stylesheets: StylistStylesheetSet::new(),
426
cascade_data: Default::default(),
427
author_styles_enabled: AuthorStylesEnabled::Yes,
428
rule_tree: RuleTree::new(),
429
num_rebuilds: 0,
430
}
431
}
432
433
/// Returns the document cascade data.
434
#[inline]
435
pub fn cascade_data(&self) -> &DocumentCascadeData {
436
&self.cascade_data
437
}
438
439
/// Returns whether author styles are enabled or not.
440
#[inline]
441
pub fn author_styles_enabled(&self) -> AuthorStylesEnabled {
442
self.author_styles_enabled
443
}
444
445
/// Iterate through all the cascade datas from the document.
446
#[inline]
447
pub fn iter_origins(&self) -> DocumentCascadeDataIter {
448
self.cascade_data.iter_origins()
449
}
450
451
/// Iterate over the extra data in origin order.
452
#[inline]
453
pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator {
454
ExtraStyleDataIterator(self.cascade_data.iter_origins())
455
}
456
457
/// Iterate over the extra data in reverse origin order.
458
#[inline]
459
pub fn iter_extra_data_origins_rev(&self) -> ExtraStyleDataIterator {
460
ExtraStyleDataIterator(self.cascade_data.iter_origins_rev())
461
}
462
463
/// Returns the number of selectors.
464
pub fn num_selectors(&self) -> usize {
465
self.cascade_data
466
.iter_origins()
467
.map(|(d, _)| d.num_selectors)
468
.sum()
469
}
470
471
/// Returns the number of declarations.
472
pub fn num_declarations(&self) -> usize {
473
self.cascade_data
474
.iter_origins()
475
.map(|(d, _)| d.num_declarations)
476
.sum()
477
}
478
479
/// Returns the number of times the stylist has been rebuilt.
480
pub fn num_rebuilds(&self) -> usize {
481
self.num_rebuilds
482
}
483
484
/// Returns the number of revalidation_selectors.
485
pub fn num_revalidation_selectors(&self) -> usize {
486
self.cascade_data
487
.iter_origins()
488
.map(|(data, _)| data.selectors_for_cache_revalidation.len())
489
.sum()
490
}
491
492
/// Returns the number of entries in invalidation maps.
493
pub fn num_invalidations(&self) -> usize {
494
self.cascade_data
495
.iter_origins()
496
.map(|(data, _)| data.invalidation_map.len())
497
.sum()
498
}
499
500
/// Returns whether the given DocumentState bit is relied upon by a selector
501
/// of some rule.
502
pub fn has_document_state_dependency(&self, state: DocumentState) -> bool {
503
self.cascade_data
504
.iter_origins()
505
.any(|(d, _)| d.document_state_dependencies.intersects(state))
506
}
507
508
/// Flush the list of stylesheets if they changed, ensuring the stylist is
509
/// up-to-date.
510
pub fn flush<E>(
511
&mut self,
512
guards: &StylesheetGuards,
513
document_element: Option<E>,
514
snapshots: Option<&SnapshotMap>,
515
) -> bool
516
where
517
E: TElement,
518
{
519
if !self.stylesheets.has_changed() {
520
return false;
521
}
522
523
self.num_rebuilds += 1;
524
525
// Update viewport_constraints regardless of which origins'
526
// `CascadeData` we're updating.
527
self.viewport_constraints = None;
528
if viewport_rule::enabled() {
529
// TODO(emilio): This doesn't look so efficient.
530
//
531
// Presumably when we properly implement this we can at least have a
532
// bit on the stylesheet that says whether it contains viewport
533
// rules to skip it entirely?
534
//
535
// Processing it with the rest of rules seems tricky since it
536
// overrides the viewport size which may change the evaluation of
537
// media queries (or may not? how are viewport units in media
538
// queries defined?)
539
let cascaded_rule = ViewportRule {
540
declarations: viewport_rule::Cascade::from_stylesheets(
541
self.stylesheets.iter(),
542
guards,
543
&self.device,
544
)
545
.finish(),
546
};
547
548
self.viewport_constraints =
549
ViewportConstraints::maybe_new(&self.device, &cascaded_rule, self.quirks_mode);
550
551
if let Some(ref constraints) = self.viewport_constraints {
552
self.device.account_for_viewport_rule(constraints);
553
}
554
}
555
556
let flusher = self.stylesheets.flush(document_element, snapshots);
557
558
let had_invalidations = flusher.had_invalidations();
559
560
self.cascade_data
561
.rebuild(&self.device, self.quirks_mode, flusher, guards)
562
.unwrap_or_else(|_| warn!("OOM in Stylist::flush"));
563
564
had_invalidations
565
}
566
567
/// Insert a given stylesheet before another stylesheet in the document.
568
pub fn insert_stylesheet_before(
569
&mut self,
570
sheet: StylistSheet,
571
before_sheet: StylistSheet,
572
guard: &SharedRwLockReadGuard,
573
) {
574
self.stylesheets
575
.insert_stylesheet_before(Some(&self.device), sheet, before_sheet, guard)
576
}
577
578
/// Marks a given stylesheet origin as dirty, due to, for example, changes
579
/// in the declarations that affect a given rule.
580
///
581
/// FIXME(emilio): Eventually it'd be nice for this to become more
582
/// fine-grained.
583
pub fn force_stylesheet_origins_dirty(&mut self, origins: OriginSet) {
584
self.stylesheets.force_dirty(origins)
585
}
586
587
/// Sets whether author style is enabled or not.
588
pub fn set_author_styles_enabled(&mut self, enabled: AuthorStylesEnabled) {
589
self.author_styles_enabled = enabled;
590
}
591
592
/// Returns whether we've recorded any stylesheet change so far.
593
pub fn stylesheets_have_changed(&self) -> bool {
594
self.stylesheets.has_changed()
595
}
596
597
/// Appends a new stylesheet to the current set.
598
pub fn append_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
599
self.stylesheets
600
.append_stylesheet(Some(&self.device), sheet, guard)
601
}
602
603
/// Remove a given stylesheet to the current set.
604
pub fn remove_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
605
self.stylesheets
606
.remove_stylesheet(Some(&self.device), sheet, guard)
607
}
608
609
/// Appends a new stylesheet to the current set.
610
#[inline]
611
pub fn sheet_count(&self, origin: Origin) -> usize {
612
self.stylesheets.sheet_count(origin)
613
}
614
615
/// Appends a new stylesheet to the current set.
616
#[inline]
617
pub fn sheet_at(&self, origin: Origin, index: usize) -> Option<&StylistSheet> {
618
self.stylesheets.get(origin, index)
619
}
620
621
/// Returns whether for any of the applicable style rule data a given
622
/// condition is true.
623
pub fn any_applicable_rule_data<E, F>(&self, element: E, mut f: F) -> bool
624
where
625
E: TElement,
626
F: FnMut(&CascadeData) -> bool,
627
{
628
if f(&self.cascade_data.user_agent.cascade_data) {
629
return true;
630
}
631
632
let mut maybe = false;
633
634
let doc_author_rules_apply =
635
element.each_applicable_non_document_style_rule_data(|data, _| {
636
maybe = maybe || f(&*data);
637
});
638
639
if maybe || f(&self.cascade_data.user) {
640
return true;
641
}
642
643
doc_author_rules_apply && f(&self.cascade_data.author)
644
}
645
646
/// Computes the style for a given "precomputed" pseudo-element, taking the
647
/// universal rules and applying them.
648
pub fn precomputed_values_for_pseudo<E>(
649
&self,
650
guards: &StylesheetGuards,
651
pseudo: &PseudoElement,
652
parent: Option<&ComputedValues>,
653
font_metrics: &dyn FontMetricsProvider,
654
) -> Arc<ComputedValues>
655
where
656
E: TElement,
657
{
658
debug_assert!(pseudo.is_precomputed());
659
660
let rule_node = self.rule_node_for_precomputed_pseudo(guards, pseudo, None);
661
662
self.precomputed_values_for_pseudo_with_rule_node::<E>(
663
guards,
664
pseudo,
665
parent,
666
font_metrics,
667
rule_node,
668
)
669
}
670
671
/// Computes the style for a given "precomputed" pseudo-element with
672
/// given rule node.
673
///
674
/// TODO(emilio): The type parameter could go away with a void type
675
/// implementing TElement.
676
pub fn precomputed_values_for_pseudo_with_rule_node<E>(
677
&self,
678
guards: &StylesheetGuards,
679
pseudo: &PseudoElement,
680
parent: Option<&ComputedValues>,
681
font_metrics: &dyn FontMetricsProvider,
682
rules: StrongRuleNode,
683
) -> Arc<ComputedValues>
684
where
685
E: TElement,
686
{
687
self.compute_pseudo_element_style_with_inputs::<E>(
688
CascadeInputs {
689
rules: Some(rules),
690
visited_rules: None,
691
},
692
pseudo,
693
guards,
694
parent,
695
font_metrics,
696
None,
697
)
698
}
699
700
/// Returns the rule node for given precomputed pseudo-element.
701
///
702
/// If we want to include extra declarations to this precomputed pseudo-element,
703
/// we can provide a vector of ApplicableDeclarationBlock to extra_declarations
704
/// argument. This is useful for providing extra @page rules.
705
pub fn rule_node_for_precomputed_pseudo(
706
&self,
707
guards: &StylesheetGuards,
708
pseudo: &PseudoElement,
709
extra_declarations: Option<Vec<ApplicableDeclarationBlock>>,
710
) -> StrongRuleNode {
711
let mut decl;
712
let declarations = match self
713
.cascade_data
714
.user_agent
715
.precomputed_pseudo_element_decls
716
.get(pseudo)
717
{
718
Some(declarations) => match extra_declarations {
719
Some(mut extra_decls) => {
720
decl = declarations.clone();
721
decl.append(&mut extra_decls);
722
Some(&decl)
723
},
724
None => Some(declarations),
725
},
726
None => extra_declarations.as_ref(),
727
};
728
729
match declarations {
730
Some(decls) => self.rule_tree.insert_ordered_rules_with_important(
731
decls.into_iter().map(|a| a.clone().for_rule_tree()),
732
guards,
733
),
734
None => self.rule_tree.root().clone(),
735
}
736
}
737
738
/// Returns the style for an anonymous box of the given type.
739
///
740
/// TODO(emilio): The type parameter could go away with a void type
741
/// implementing TElement.
742
#[cfg(feature = "servo")]
743
pub fn style_for_anonymous<E>(
744
&self,
745
guards: &StylesheetGuards,
746
pseudo: &PseudoElement,
747
parent_style: &ComputedValues,
748
) -> Arc<ComputedValues>
749
where
750
E: TElement,
751
{
752
use crate::font_metrics::ServoMetricsProvider;
753
self.precomputed_values_for_pseudo::<E>(
754
guards,
755
&pseudo,
756
Some(parent_style),
757
&ServoMetricsProvider,
758
)
759
}
760
761
/// Computes a pseudo-element style lazily during layout.
762
///
763
/// This can only be done for a certain set of pseudo-elements, like
764
/// :selection.
765
///
766
/// Check the documentation on lazy pseudo-elements in
767
/// docs/components/style.md
768
pub fn lazily_compute_pseudo_element_style<E>(
769
&self,
770
guards: &StylesheetGuards,
771
element: E,
772
pseudo: &PseudoElement,
773
rule_inclusion: RuleInclusion,
774
parent_style: &ComputedValues,
775
is_probe: bool,
776
font_metrics: &dyn FontMetricsProvider,
777
matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
778
) -> Option<Arc<ComputedValues>>
779
where
780
E: TElement,
781
{
782
let cascade_inputs = self.lazy_pseudo_rules(
783
guards,
784
element,
785
parent_style,
786
pseudo,
787
is_probe,
788
rule_inclusion,
789
matching_fn,
790
)?;
791
792
Some(self.compute_pseudo_element_style_with_inputs(
793
cascade_inputs,
794
pseudo,
795
guards,
796
Some(parent_style),
797
font_metrics,
798
Some(element),
799
))
800
}
801
802
/// Computes a pseudo-element style lazily using the given CascadeInputs.
803
/// This can be used for truly lazy pseudo-elements or to avoid redoing
804
/// selector matching for eager pseudo-elements when we need to recompute
805
/// their style with a new parent style.
806
pub fn compute_pseudo_element_style_with_inputs<E>(
807
&self,
808
inputs: CascadeInputs,
809
pseudo: &PseudoElement,
810
guards: &StylesheetGuards,
811
parent_style: Option<&ComputedValues>,
812
font_metrics: &dyn FontMetricsProvider,
813
element: Option<E>,
814
) -> Arc<ComputedValues>
815
where
816
E: TElement,
817
{
818
// FIXME(emilio): The lack of layout_parent_style here could be
819
// worrying, but we're probably dropping the display fixup for
820
// pseudos other than before and after, so it's probably ok.
821
//
822
// (Though the flags don't indicate so!)
823
//
824
// It'd be fine to assert that this isn't called with a parent style
825
// where display contents is in effect, but in practice this is hard to
826
// do for stuff like :-moz-fieldset-content with a
827
// <fieldset style="display: contents">. That is, the computed value of
828
// display for the fieldset is "contents", even though it's not the used
829
// value, so we don't need to adjust in a different way anyway.
830
self.cascade_style_and_visited(
831
element,
832
Some(pseudo),
833
inputs,
834
guards,
835
parent_style,
836
parent_style,
837
parent_style,
838
font_metrics,
839
/* rule_cache = */ None,
840
&mut RuleCacheConditions::default(),
841
)
842
}
843
844
/// Computes a style using the given CascadeInputs. This can be used to
845
/// compute a style any time we know what rules apply and just need to use
846
/// the given parent styles.
847
///
848
/// parent_style is the style to inherit from for properties affected by
849
/// first-line ancestors.
850
///
851
/// parent_style_ignoring_first_line is the style to inherit from for
852
/// properties not affected by first-line ancestors.
853
///
854
/// layout_parent_style is the style used for some property fixups. It's
855
/// the style of the nearest ancestor with a layout box.
856
pub fn cascade_style_and_visited<E>(
857
&self,
858
element: Option<E>,
859
pseudo: Option<&PseudoElement>,
860
inputs: CascadeInputs,
861
guards: &StylesheetGuards,
862
parent_style: Option<&ComputedValues>,
863
parent_style_ignoring_first_line: Option<&ComputedValues>,
864
layout_parent_style: Option<&ComputedValues>,
865
font_metrics: &dyn FontMetricsProvider,
866
rule_cache: Option<&RuleCache>,
867
rule_cache_conditions: &mut RuleCacheConditions,
868
) -> Arc<ComputedValues>
869
where
870
E: TElement,
871
{
872
debug_assert!(pseudo.is_some() || element.is_some(), "Huh?");
873
874
// We need to compute visited values if we have visited rules or if our
875
// parent has visited values.
876
let visited_rules = match inputs.visited_rules.as_ref() {
877
Some(rules) => Some(rules),
878
None => {
879
if parent_style.and_then(|s| s.visited_style()).is_some() {
880
Some(inputs.rules.as_ref().unwrap_or(self.rule_tree.root()))
881
} else {
882
None
883
}
884
},
885
};
886
887
// Read the comment on `precomputed_values_for_pseudo` to see why it's
888
// difficult to assert that display: contents nodes never arrive here
889
// (tl;dr: It doesn't apply for replaced elements and such, but the
890
// computed value is still "contents").
891
//
892
// FIXME(emilio): We should assert that it holds if pseudo.is_none()!
893
properties::cascade::<E>(
894
&self.device,
895
pseudo,
896
inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
897
guards,
898
parent_style,
899
parent_style_ignoring_first_line,
900
layout_parent_style,
901
visited_rules,
902
font_metrics,
903
self.quirks_mode,
904
rule_cache,
905
rule_cache_conditions,
906
element,
907
)
908
}
909
910
/// Computes the cascade inputs for a lazily-cascaded pseudo-element.
911
///
912
/// See the documentation on lazy pseudo-elements in
913
/// docs/components/style.md
914
fn lazy_pseudo_rules<E>(
915
&self,
916
guards: &StylesheetGuards,
917
element: E,
918
parent_style: &ComputedValues,
919
pseudo: &PseudoElement,
920
is_probe: bool,
921
rule_inclusion: RuleInclusion,
922
matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
923
) -> Option<CascadeInputs>
924
where
925
E: TElement,
926
{
927
debug_assert!(pseudo.is_lazy());
928
929
// Apply the selector flags. We should be in sequential mode
930
// already, so we can directly apply the parent flags.
931
let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
932
if cfg!(feature = "servo") {
933
// Servo calls this function from the worker, but only for internal
934
// pseudos, so we should never generate selector flags here.
935
unreachable!("internal pseudo generated slow selector flags?");
936
}
937
938
// No need to bother setting the selector flags when we're computing
939
// default styles.
940
if rule_inclusion == RuleInclusion::DefaultOnly {
941
return;
942
}
943
944
// Gecko calls this from sequential mode, so we can directly apply
945
// the flags.
946
debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT);
947
let self_flags = flags.for_self();
948
if !self_flags.is_empty() {
949
unsafe {
950
element.set_selector_flags(self_flags);
951
}
952
}
953
let parent_flags = flags.for_parent();
954
if !parent_flags.is_empty() {
955
if let Some(p) = element.parent_element() {
956
unsafe {
957
p.set_selector_flags(parent_flags);
958
}
959
}
960
}
961
};
962
963
let mut declarations = ApplicableDeclarationList::new();
964
let mut matching_context = MatchingContext::new(
965
MatchingMode::ForStatelessPseudoElement,
966
None,
967
None,
968
self.quirks_mode,
969
);
970
971
matching_context.pseudo_element_matching_fn = matching_fn;
972
973
self.push_applicable_declarations(
974
element,
975
Some(&pseudo),
976
None,
977
None,
978
AnimationRules(None, None),
979
rule_inclusion,
980
&mut declarations,
981
&mut matching_context,
982
&mut set_selector_flags,
983
);
984
985
if declarations.is_empty() && is_probe {
986
return None;
987
}
988
989
let rules = self.rule_tree.compute_rule_node(&mut declarations, guards);
990
991
let mut visited_rules = None;
992
if parent_style.visited_style().is_some() {
993
let mut declarations = ApplicableDeclarationList::new();
994
let mut matching_context = MatchingContext::new_for_visited(
995
MatchingMode::ForStatelessPseudoElement,
996
None,
997
None,
998
VisitedHandlingMode::RelevantLinkVisited,
999
self.quirks_mode,
1000
);
1001
matching_context.pseudo_element_matching_fn = matching_fn;
1002
1003
self.push_applicable_declarations(
1004
element,
1005
Some(&pseudo),
1006
None,
1007
None,
1008
AnimationRules(None, None),
1009
rule_inclusion,
1010
&mut declarations,
1011
&mut matching_context,
1012
&mut set_selector_flags,
1013
);
1014
if !declarations.is_empty() {
1015
let rule_node = self.rule_tree.insert_ordered_rules_with_important(
1016
declarations.drain().map(|a| a.for_rule_tree()),
1017
guards,
1018
);
1019
if rule_node != *self.rule_tree.root() {
1020
visited_rules = Some(rule_node);
1021
}
1022
}
1023
}
1024
1025
Some(CascadeInputs {
1026
rules: Some(rules),
1027
visited_rules,
1028
})
1029
}
1030
1031
/// Set a given device, which may change the styles that apply to the
1032
/// document.
1033
///
1034
/// Returns the sheet origins that were actually affected.
1035
///
1036
/// This means that we may need to rebuild style data even if the
1037
/// stylesheets haven't changed.
1038
///
1039
/// Also, the device that arrives here may need to take the viewport rules
1040
/// into account.
1041
pub fn set_device(&mut self, mut device: Device, guards: &StylesheetGuards) -> OriginSet {
1042
if viewport_rule::enabled() {
1043
let cascaded_rule = {
1044
let stylesheets = self.stylesheets.iter();
1045
1046
ViewportRule {
1047
declarations: viewport_rule::Cascade::from_stylesheets(
1048
stylesheets,
1049
guards,
1050
&device,
1051
)
1052
.finish(),
1053
}
1054
};
1055
1056
self.viewport_constraints =
1057
ViewportConstraints::maybe_new(&device, &cascaded_rule, self.quirks_mode);
1058
1059
if let Some(ref constraints) = self.viewport_constraints {
1060
device.account_for_viewport_rule(constraints);
1061
}
1062
}
1063
1064
self.device = device;
1065
self.media_features_change_changed_style(guards, &self.device)
1066
}
1067
1068
/// Returns whether, given a media feature change, any previously-applicable
1069
/// style has become non-applicable, or vice-versa for each origin, using
1070
/// `device`.
1071
pub fn media_features_change_changed_style(
1072
&self,
1073
guards: &StylesheetGuards,
1074
device: &Device,
1075
) -> OriginSet {
1076
debug!("Stylist::media_features_change_changed_style {:?}", device);
1077
1078
let mut origins = OriginSet::empty();
1079
let stylesheets = self.stylesheets.iter();
1080
1081
for (stylesheet, origin) in stylesheets {
1082
if origins.contains(origin.into()) {
1083
continue;
1084
}
1085
1086
let guard = guards.for_origin(origin);
1087
let origin_cascade_data = self.cascade_data.borrow_for_origin(origin);
1088
1089
let affected_changed = !origin_cascade_data.media_feature_affected_matches(
1090
stylesheet,
1091
guard,
1092
device,
1093
self.quirks_mode,
1094
);
1095
1096
if affected_changed {
1097
origins |= origin;
1098
}
1099
}
1100
1101
origins
1102
}
1103
1104
/// Returns the viewport constraints that apply to this document because of
1105
/// a @viewport rule.
1106
pub fn viewport_constraints(&self) -> Option<&ViewportConstraints> {
1107
self.viewport_constraints.as_ref()
1108
}
1109
1110
/// Returns the Quirks Mode of the document.
1111
pub fn quirks_mode(&self) -> QuirksMode {
1112
self.quirks_mode
1113
}
1114
1115
/// Sets the quirks mode of the document.
1116
pub fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
1117
if self.quirks_mode == quirks_mode {
1118
return;
1119
}
1120
self.quirks_mode = quirks_mode;
1121
self.force_stylesheet_origins_dirty(OriginSet::all());
1122
}
1123
1124
/// Returns the applicable CSS declarations for the given element.
1125
pub fn push_applicable_declarations<E, F>(
1126
&self,
1127
element: E,
1128
pseudo_element: Option<&PseudoElement>,
1129
style_attribute: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
1130
smil_override: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
1131
animation_rules: AnimationRules,
1132
rule_inclusion: RuleInclusion,
1133
applicable_declarations: &mut ApplicableDeclarationList,
1134
context: &mut MatchingContext<E::Impl>,
1135
flags_setter: &mut F,
1136
) where
1137
E: TElement,
1138
F: FnMut(&E, ElementSelectorFlags),
1139
{
1140
RuleCollector::new(
1141
self,
1142
element,
1143
pseudo_element,
1144
style_attribute,
1145
smil_override,
1146
animation_rules,
1147
rule_inclusion,
1148
applicable_declarations,
1149
context,
1150
flags_setter,
1151
)
1152
.collect_all();
1153
}
1154
1155
/// Given an id, returns whether there might be any rules for that id in any
1156
/// of our rule maps.
1157
#[inline]
1158
pub fn may_have_rules_for_id<E>(&self, id: &WeakAtom, element: E) -> bool
1159
where
1160
E: TElement,
1161
{
1162
// If id needs to be compared case-insensitively, the logic below
1163
// wouldn't work. Just conservatively assume it may have such rules.
1164
match self.quirks_mode().classes_and_ids_case_sensitivity() {
1165
CaseSensitivity::AsciiCaseInsensitive => return true,
1166
CaseSensitivity::CaseSensitive => {},
1167
}
1168
1169
self.any_applicable_rule_data(element, |data| data.mapped_ids.contains(id))
1170
}
1171
1172
/// Returns the registered `@keyframes` animation for the specified name.
1173
#[inline]
1174
pub fn get_animation<'a, E>(&'a self, name: &Atom, element: E) -> Option<&'a KeyframesAnimation>
1175
where
1176
E: TElement + 'a,
1177
{
1178
macro_rules! try_find_in {
1179
($data:expr) => {
1180
if let Some(animation) = $data.animations.get(name) {
1181
return Some(animation);
1182
}
1183
};
1184
}
1185
1186
// NOTE(emilio): We implement basically what Blink does for this case,
1187
// which is [1] as of this writing.
1188
//
1189
// See [2] for the spec discussion about what to do about this. WebKit's
1190
// behavior makes a bit more sense off-hand, but it's way more complex
1191
// to implement, and it makes value computation having to thread around
1192
// the cascade level, which is not great. Also, it breaks if you inherit
1193
// animation-name from an element in a different tree.
1194
//
1195
// See [3] for the bug to implement whatever gets resolved, and related
1196
// bugs for a bit more context.
1197
//
1198
// FIXME(emilio): This should probably work for pseudo-elements (i.e.,
1199
// use rule_hash_target().shadow_root() instead of
1200
// element.shadow_root()).
1201
//
1203
// core/css/resolver/style_resolver.cc?l=1267&rcl=90f9f8680ebb4a87d177f3b0833372ae4e0c88d8
1206
if let Some(shadow) = element.shadow_root() {
1207
if let Some(data) = shadow.style_data() {
1208
try_find_in!(data);
1209
}
1210
}
1211
1212
// Use the same rules to look for the containing host as we do for rule
1213
// collection.
1214
if let Some(shadow) = containing_shadow_ignoring_svg_use(element) {
1215
if let Some(data) = shadow.style_data() {
1216
try_find_in!(data);
1217
}
1218
} else {
1219
try_find_in!(self.cascade_data.author);
1220
}
1221
1222
try_find_in!(self.cascade_data.user);
1223
try_find_in!(self.cascade_data.user_agent.cascade_data);
1224
1225
None
1226
}
1227
1228
/// Computes the match results of a given element against the set of
1229
/// revalidation selectors.
1230
pub fn match_revalidation_selectors<E, F>(
1231
&self,
1232
element: E,
1233
bloom: Option<&BloomFilter>,
1234
nth_index_cache: &mut NthIndexCache,
1235
flags_setter: &mut F,
1236
) -> SmallBitVec
1237
where
1238
E: TElement,
1239
F: FnMut(&E, ElementSelectorFlags),
1240
{
1241
// NB: `MatchingMode` doesn't really matter, given we don't share style
1242
// between pseudos.
1243
let mut matching_context = MatchingContext::new(
1244
MatchingMode::Normal,
1245
bloom,
1246
Some(nth_index_cache),
1247
self.quirks_mode,
1248
);
1249
1250
// Note that, by the time we're revalidating, we're guaranteed that the
1251
// candidate and the entry have the same id, classes, and local name.
1252
// This means we're guaranteed to get the same rulehash buckets for all
1253
// the lookups, which means that the bitvecs are comparable. We verify
1254
// this in the caller by asserting that the bitvecs are same-length.
1255
let mut results = SmallBitVec::new();
1256
1257
let matches_document_rules =
1258
element.each_applicable_non_document_style_rule_data(|data, host| {
1259
matching_context.with_shadow_host(Some(host), |matching_context| {
1260
data.selectors_for_cache_revalidation.lookup(
1261
element,
1262
self.quirks_mode,
1263
|selector_and_hashes| {
1264
results.push(matches_selector(
1265
&selector_and_hashes.selector,
1266
selector_and_hashes.selector_offset,
1267
Some(&selector_and_hashes.hashes),
1268
&element,
1269
matching_context,
1270
flags_setter,
1271
));
1272
true
1273
},
1274
);
1275
})
1276
});
1277
1278
for (data, origin) in self.cascade_data.iter_origins() {
1279
if origin == Origin::Author && !matches_document_rules {
1280
continue;
1281
}
1282
1283
data.selectors_for_cache_revalidation.lookup(
1284
element,
1285
self.quirks_mode,
1286
|selector_and_hashes| {
1287
results.push(matches_selector(
1288
&selector_and_hashes.selector,
1289
selector_and_hashes.selector_offset,
1290
Some(&selector_and_hashes.hashes),
1291
&element,
1292
&mut matching_context,
1293
flags_setter,
1294
));
1295
true
1296
},
1297
);
1298
}
1299
1300
results
1301
}
1302
1303
/// Computes styles for a given declaration with parent_style.
1304
///
1305
/// FIXME(emilio): the lack of pseudo / cascade flags look quite dubious,
1306
/// hopefully this is only used for some canvas font stuff.
1307
///
1308
/// TODO(emilio): The type parameter can go away when
1310
pub fn compute_for_declarations<E>(
1311
&self,
1312
guards: &StylesheetGuards,
1313
parent_style: &ComputedValues,
1314
declarations: Arc<Locked<PropertyDeclarationBlock>>,
1315
) -> Arc<ComputedValues>
1316
where
1317
E: TElement,
1318
{
1319
use crate::font_metrics::get_metrics_provider_for_product;
1320
1321
let block = declarations.read_with(guards.author);
1322
let iter_declarations = || {
1323
block
1324
.declaration_importance_iter()
1325
.map(|(declaration, importance)| {
1326
debug_assert!(!importance.important());
1327
(declaration, CascadeLevel::StyleAttributeNormal)
1328
})
1329
};
1330
1331
let metrics = get_metrics_provider_for_product();
1332
1333
// We don't bother inserting these declarations in the rule tree, since
1334
// it'd be quite useless and slow.
1335
properties::apply_declarations::<E, _, _>(
1336
&self.device,
1337
/* pseudo = */ None,
1338
self.rule_tree.root(),
1339
guards,
1340
iter_declarations,
1341
Some(parent_style),
1342
Some(parent_style),
1343
Some(parent_style),
1344
&metrics,
1345
CascadeMode::Unvisited {
1346
visited_rules: None,
1347
},
1348
self.quirks_mode,
1349
/* rule_cache = */ None,
1350
&mut Default::default(),
1351
/* element = */ None,
1352
)
1353
}
1354
1355
/// Accessor for a shared reference to the device.
1356
#[inline]
1357
pub fn device(&self) -> &Device {
1358
&self.device
1359
}
1360
1361
/// Accessor for a mutable reference to the device.
1362
#[inline]
1363
pub fn device_mut(&mut self) -> &mut Device {
1364
&mut self.device
1365
}
1366
1367
/// Accessor for a shared reference to the rule tree.
1368
#[inline]
1369
pub fn rule_tree(&self) -> &RuleTree {
1370
&self.rule_tree
1371
}
1372
1373
/// Measures heap usage.
1374
#[cfg(feature = "gecko")]
1375
pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
1376
self.cascade_data.add_size_of(ops, sizes);
1377
sizes.mRuleTree += self.rule_tree.size_of(ops);
1378
1379
// We may measure other fields in the future if DMD says it's worth it.
1380
}
1381
1382
/// Shutdown the static data that this module stores.
1383
pub fn shutdown() {
1384
let _entries = UA_CASCADE_DATA_CACHE.lock().unwrap().take_all();
1385
}
1386
}
1387
1388
/// This struct holds data which users of Stylist may want to extract
1389
/// from stylesheets which can be done at the same time as updating.
1390
#[derive(Debug, Default)]
1391
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
1392
pub struct ExtraStyleData {
1393
/// A list of effective font-face rules and their origin.
1394
#[cfg(feature = "gecko")]
1395
pub font_faces: Vec<Arc<Locked<FontFaceRule>>>,
1396
1397
/// A list of effective font-feature-values rules.
1398
#[cfg(feature = "gecko")]
1399
pub font_feature_values: Vec<Arc<Locked<FontFeatureValuesRule>>>,
1400
1401
/// A map of effective counter-style rules.
1402
#[cfg(feature = "gecko")]
1403
pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
1404
1405
/// A map of effective page rules.
1406
#[cfg(feature = "gecko")]
1407
pub pages: Vec<Arc<Locked<PageRule>>>,
1408
}
1409
1410
#[cfg(feature = "gecko")]
1411
unsafe impl Sync for ExtraStyleData {}
1412
#[cfg(feature = "gecko")]
1413
unsafe impl Send for ExtraStyleData {}
1414
1415
#[cfg(feature = "gecko")]
1416
impl ExtraStyleData {
1417
/// Add the given @font-face rule.
1418
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
1419
self.font_faces.push(rule.clone());
1420
}
1421
1422
/// Add the given @font-feature-values rule.
1423
fn add_font_feature_values(&mut self, rule: &Arc<Locked<FontFeatureValuesRule>>) {
1424
self.font_feature_values.push(rule.clone());
1425
}
1426
1427
/// Add the given @counter-style rule.
1428
fn add_counter_style(
1429
&mut self,
1430
guard: &SharedRwLockReadGuard,
1431
rule: &Arc<Locked<CounterStyleRule>>,
1432
) {
1433
let name = rule.read_with(guard).name().0.clone();
1434
self.counter_styles.insert(name, rule.clone());
1435
}
1436
1437
/// Add the given @page rule.
1438
fn add_page(&mut self, rule: &Arc<Locked<PageRule>>) {
1439
self.pages.push(rule.clone());
1440
}
1441
}
1442
1443
impl ExtraStyleData {
1444
fn clear(&mut self) {
1445
#[cfg(feature = "gecko")]
1446
{
1447
self.font_faces.clear();
1448
self.font_feature_values.clear();
1449
self.counter_styles.clear();
1450
self.pages.clear();
1451
}
1452
}
1453
}
1454
1455
/// An iterator over the different ExtraStyleData.
1456
pub struct ExtraStyleDataIterator<'a>(DocumentCascadeDataIter<'a>);
1457
1458
impl<'a> Iterator for ExtraStyleDataIterator<'a> {
1459
type Item = (&'a ExtraStyleData, Origin);
1460
1461
fn next(&mut self) -> Option<Self::Item> {
1462
self.0.next().map(|d| (&d.0.extra_data, d.1))
1463
}
1464
}
1465
1466
#[cfg(feature = "gecko")]
1467
impl MallocSizeOf for ExtraStyleData {
1468
/// Measure heap usage.
1469
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
1470
let mut n = 0;
1471
n += self.font_faces.shallow_size_of(ops);
1472
n += self.font_feature_values.shallow_size_of(ops);
1473
n += self.counter_styles.shallow_size_of(ops);
1474
n += self.pages.shallow_size_of(ops);
1475
n
1476
}
1477
}
1478
1479
/// SelectorMapEntry implementation for use in our revalidation selector map.
1480
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1481
#[derive(Clone, Debug)]
1482
struct RevalidationSelectorAndHashes {
1483
#[cfg_attr(
1484
feature = "gecko",
1485
ignore_malloc_size_of = "CssRules have primary refs, we measure there"
1486
)]
1487
selector: Selector<SelectorImpl>,
1488
selector_offset: usize,
1489
hashes: AncestorHashes,
1490
}
1491
1492
impl RevalidationSelectorAndHashes {
1493
fn new(selector: Selector<SelectorImpl>, hashes: AncestorHashes) -> Self {
1494
let selector_offset = {
1495
// We basically want to check whether the first combinator is a
1496
// pseudo-element combinator. If it is, we want to use the offset
1497
// one past it. Otherwise, our offset is 0.
1498
let mut index = 0;
1499
let mut iter = selector.iter();
1500
1501
// First skip over the first ComplexSelector.
1502
//
1503
// We can't check what sort of what combinator we have until we do
1504
// that.
1505
for _ in &mut iter {
1506
index += 1; // Simple selector
1507
}
1508
1509
match iter.next_sequence() {
1510
Some(Combinator::PseudoElement) => index + 1, // +1 for the combinator
1511
_ => 0,
1512
}
1513
};
1514
1515
RevalidationSelectorAndHashes {
1516
selector,
1517
selector_offset,
1518
hashes,
1519
}
1520
}
1521
}
1522
1523
impl SelectorMapEntry for RevalidationSelectorAndHashes {
1524
fn selector(&self) -> SelectorIter<SelectorImpl> {
1525
self.selector.iter_from(self.selector_offset)
1526
}
1527
}
1528
1529
/// A selector visitor implementation that collects all the state the Stylist
1530
/// cares about a selector.
1531
struct StylistSelectorVisitor<'a> {
1532
/// Whether the selector needs revalidation for the style sharing cache.
1533
needs_revalidation: bool,
1534
/// Whether we've past the rightmost compound selector, not counting
1535
/// pseudo-elements.
1536
passed_rightmost_selector: bool,
1537
/// The filter with all the id's getting referenced from rightmost
1538
/// selectors.
1539
mapped_ids: &'a mut PrecomputedHashSet<Atom>,
1540
/// The filter with the local names of attributes there are selectors for.
1541
attribute_dependencies: &'a mut PrecomputedHashSet<LocalName>,
1542
/// All the states selectors in the page reference.
1543
state_dependencies: &'a mut ElementState,
1544
/// All the document states selectors in the page reference.
1545
document_state_dependencies: &'a mut DocumentState,
1546
}
1547
1548
fn component_needs_revalidation(
1549
c: &Component<SelectorImpl>,
1550
passed_rightmost_selector: bool,
1551
) -> bool {
1552
match *c {
1553
Component::ID(_) => {
1554
// TODO(emilio): This could also check that the ID is not already in
1555
// the rule hash. In that case, we could avoid making this a
1556
// revalidation selector too.
1557
//
1559
passed_rightmost_selector
1560
},
1561
Component::AttributeInNoNamespaceExists { .. } |
1562
Component::AttributeInNoNamespace { .. } |
1563
Component::AttributeOther(_) |
1564
Component::Empty |
1565
Component::FirstChild |
1566
Component::LastChild |
1567
Component::OnlyChild |
1568
Component::NthChild(..) |
1569
Component::NthLastChild(..) |
1570
Component::NthOfType(..) |
1571
Component::NthLastOfType(..) |
1572
Component::FirstOfType |
1573
Component::LastOfType |
1574
Component::OnlyOfType => true,
1575
Component::NonTSPseudoClass(ref p) => p.needs_cache_revalidation(),
1576
_ => false,
1577
}
1578
}
1579
1580
impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
1581
type Impl = SelectorImpl;
1582
1583
fn visit_complex_selector(&mut self, combinator: Option<Combinator>) -> bool {
1584
self.needs_revalidation =
1585
self.needs_revalidation || combinator.map_or(false, |c| c.is_sibling());
1586
1587
// NOTE(emilio): This works properly right now because we can't store
1588
// complex selectors in nested selectors, otherwise we may need to
1589
// rethink this.
1590
//
1591
// Also, note that this call happens before we visit any of the simple
1592
// selectors in the next ComplexSelector, so we can use this to skip
1593
// looking at them.
1594
self.passed_rightmost_selector = self.passed_rightmost_selector ||
1595
!matches!(combinator, None | Some(Combinator::PseudoElement));
1596
1597
true
1598
}
1599
1600
fn visit_attribute_selector(
1601
&mut self,
1602
_ns: &NamespaceConstraint<&Namespace>,
1603
name: &LocalName,
1604
lower_name: &LocalName,
1605
) -> bool {
1606
self.attribute_dependencies.insert(name.clone());
1607
self.attribute_dependencies.insert(lower_name.clone());
1608
true
1609
}
1610
1611
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
1612
self.needs_revalidation = self.needs_revalidation ||
1613
component_needs_revalidation(s, self.passed_rightmost_selector);
1614
1615
match *s {
1616
Component::NonTSPseudoClass(ref p) => {
1617
self.state_dependencies.insert(p.state_flag());
1618
self.document_state_dependencies
1619
.insert(p.document_state_flag());
1620
},
1621
Component::ID(ref id) if !self.passed_rightmost_selector => {
1622
// We want to stop storing mapped ids as soon as we've moved off
1623
// the rightmost ComplexSelector that is not a pseudo-element.
1624
//
1625
// That can be detected by a visit_complex_selector call with a
1626
// combinator other than None and PseudoElement.
1627
//
1628
// Importantly, this call happens before we visit any of the
1629
// simple selectors in that ComplexSelector.
1630
//
1631
// NOTE(emilio): See the comment regarding on when this may
1632
// break in visit_complex_selector.
1633
self.mapped_ids.insert(id.clone());
1634
},
1635
_ => {},
1636
}
1637
1638
true
1639
}
1640
}
1641
1642
/// A set of rules for element and pseudo-elements.
1643
#[derive(Debug, Default, MallocSizeOf)]
1644
struct GenericElementAndPseudoRules<Map> {
1645
/// Rules from stylesheets at this `CascadeData`'s origin.
1646
element_map: Map,
1647
1648
/// Rules from stylesheets at this `CascadeData`'s origin that correspond
1649
/// to a given pseudo-element.
1650
///
1651
/// FIXME(emilio): There are a bunch of wasted entries here in practice.
1652
/// Figure out a good way to do a `PerNonAnonBox` and `PerAnonBox` (for
1653
/// `precomputed_values_for_pseudo`) without duplicating a lot of code.
1654
pseudos_map: PerPseudoElementMap<Box<Map>>,
1655
}
1656
1657
impl<Map: Default + MallocSizeOf> GenericElementAndPseudoRules<Map> {
1658
#[inline(always)]
1659
fn for_insertion(&mut self, pseudo_element: Option<&PseudoElement>) -> &mut Map {
1660
debug_assert!(
1661
pseudo_element.map_or(true, |pseudo| {
1662
!pseudo.is_precomputed() && !pseudo.is_unknown_webkit_pseudo_element()
1663
}),
1664
"Precomputed pseudos should end up in precomputed_pseudo_element_decls, \
1665
and unknown webkit pseudos should be discarded before getting here"
1666
);
1667
1668
match pseudo_element {
1669
None => &mut self.element_map,
1670
Some(pseudo) => self
1671
.pseudos_map
1672
.get_or_insert_with(pseudo, || Box::new(Default::default())),
1673
}
1674
}
1675
1676
#[inline]
1677
fn rules(&self, pseudo: Option<&PseudoElement>) -> Option<&Map> {
1678
match pseudo {
1679
Some(pseudo) => self.pseudos_map.get(pseudo).map(|p| &**p),
1680
None => Some(&self.element_map),
1681
}
1682
}
1683
1684
/// Measures heap usage.
1685
#[cfg(feature = "gecko")]
1686
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
1687
sizes.mElementAndPseudosMaps += self.element_map.size_of(ops);
1688
1689
for elem in self.pseudos_map.iter() {
1690
if let Some(ref elem) = *elem {
1691
sizes.mElementAndPseudosMaps += <Box<_> as MallocSizeOf>::size_of(elem, ops);
1692
}
1693
}
1694
}
1695
}
1696
1697
type ElementAndPseudoRules = GenericElementAndPseudoRules<SelectorMap<Rule>>;
1698
type PartMap = PrecomputedHashMap<Atom, SmallVec<[Rule; 1]>>;
1699
type PartElementAndPseudoRules = GenericElementAndPseudoRules<PartMap>;
1700
1701
impl ElementAndPseudoRules {
1702
// TODO(emilio): Should we retain storage of these?
1703
fn clear(&mut self) {
1704
self.element_map.clear();
1705
self.pseudos_map.clear();
1706
}
1707
}
1708
1709
impl PartElementAndPseudoRules {
1710
// TODO(emilio): Should we retain storage of these?
1711
fn clear(&mut self) {
1712
self.element_map.clear();
1713
self.pseudos_map.clear();
1714
}
1715
}
1716
1717
/// Data resulting from performing the CSS cascade that is specific to a given
1718
/// origin.
1719
///
1720
/// FIXME(emilio): Consider renaming and splitting in `CascadeData` and
1721
/// `InvalidationData`? That'd make `clear_cascade_data()` clearer.
1722
#[derive(Debug, MallocSizeOf)]
1723
pub struct CascadeData {
1724
/// The data coming from normal style rules that apply to elements at this
1725
/// cascade level.
1726
normal_rules: ElementAndPseudoRules,
1727
1728
/// The `:host` pseudo rules that are the rightmost selector (without
1729
/// accounting for pseudo-elements).
1730
host_rules: Option<Box<ElementAndPseudoRules>>,
1731
1732
/// The data coming from ::slotted() pseudo-element rules.
1733
///
1734
/// We need to store them separately because an element needs to match
1735
/// ::slotted() pseudo-element rules in different shadow roots.
1736
///
1737
/// In particular, we need to go through all the style data in all the
1738
/// containing style scopes starting from the closest assigned slot.
1739
slotted_rules: Option<Box<ElementAndPseudoRules>>,
1740
1741
/// The data coming from ::part() pseudo-element rules.
1742
///
1743
/// We need to store them separately because an element needs to match
1744
/// ::part() pseudo-element rules in different shadow roots.
1745
part_rules: Option<Box<PartElementAndPseudoRules>>,
1746
1747
/// The invalidation map for these rules.
1748
invalidation_map: InvalidationMap,
1749
1750
/// The attribute local names that appear in attribute selectors. Used
1751
/// to avoid taking element snapshots when an irrelevant attribute changes.
1752
/// (We don't bother storing the namespace, since namespaced attributes are
1753
/// rare.)
1754
attribute_dependencies: PrecomputedHashSet<LocalName>,
1755
1756
/// The element state bits that are relied on by selectors. Like
1757
/// `attribute_dependencies`, this is used to avoid taking element snapshots
1758
/// when an irrelevant element state bit changes.
1759
state_dependencies: ElementState,
1760
1761
/// The document state bits that are relied on by selectors. This is used
1762
/// to tell whether we need to restyle the entire document when a document
1763
/// state bit changes.
1764
document_state_dependencies: DocumentState,
1765
1766
/// The ids that appear in the rightmost complex selector of selectors (and
1767
/// hence in our selector maps). Used to determine when sharing styles is
1768
/// safe: we disallow style sharing for elements whose id matches this
1769
/// filter, and hence might be in one of our selector maps.
1770
mapped_ids: PrecomputedHashSet<Atom>,
1771
1772
/// Selectors that require explicit cache revalidation (i.e. which depend
1773
/// on state that is not otherwise visible to the cache, like attributes or
1774
/// tree-structural state like child index and pseudos).
1775
#[ignore_malloc_size_of = "Arc"]
1776
selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,
1777
1778
/// A map with all the animations at this `CascadeData`'s origin, indexed
1779
/// by name.
1780
animations: PrecomputedHashMap<Atom, KeyframesAnimation>,
1781
1782
/// Effective media query results cached from the last rebuild.
1783
effective_media_query_results: EffectiveMediaQueryResults,
1784
1785
/// Extra data, like different kinds of rules, etc.
1786
extra_data: ExtraStyleData,
1787
1788
/// A monotonically increasing counter to represent the order on which a
1789
/// style rule appears in a stylesheet, needed to sort them by source order.
1790
rules_source_order: u32,
1791
1792
/// The total number of selectors.
1793
num_selectors: usize,
1794
1795
/// The total number of declarations.
1796
num_declarations: usize,
1797
}
1798
1799
impl CascadeData {
1800
/// Creates an empty `CascadeData`.
1801
pub fn new() -> Self {
1802
Self {
1803
normal_rules: ElementAndPseudoRules::default(),
1804
host_rules: None,
1805
slotted_rules: None,
1806
part_rules: None,
1807
invalidation_map: InvalidationMap::new(),
1808
attribute_dependencies: PrecomputedHashSet::default(),
1809
state_dependencies: ElementState::empty(),
1810
document_state_dependencies: DocumentState::empty(),
1811
mapped_ids: PrecomputedHashSet::default(),
1812
selectors_for_cache_revalidation: SelectorMap::new(),
1813
animations: Default::default(),
1814
extra_data: ExtraStyleData::default(),
1815
effective_media_query_results: EffectiveMediaQueryResults::new(),
1816
rules_source_order: 0,
1817
num_selectors: 0,
1818
num_declarations: 0,
1819
}
1820
}
1821
1822
/// Rebuild the cascade data from a given SheetCollection, incrementally if
1823
/// possible.
1824
pub fn rebuild<'a, S>(
1825
&mut self,
1826
device: &Device,
1827
quirks_mode: QuirksMode,
1828
collection: SheetCollectionFlusher<S>,
1829
guard: &SharedRwLockReadGuard,
1830
) -> Result<(), FailedAllocationError>
1831
where
1832
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
1833
{
1834
if !collection.dirty() {
1835
return Ok(());
1836
}
1837
1838
let validity = collection.data_validity();
1839
1840
match validity {
1841
DataValidity::Valid => {},
1842
DataValidity::CascadeInvalid => self.clear_cascade_data(),
1843
DataValidity::FullyInvalid => self.clear(),
1844
}
1845
1846
for (stylesheet, rebuild_kind) in collection {
1847
self.add_stylesheet(
1848
device,
1849
quirks_mode,
1850
stylesheet,
1851
guard,
1852
rebuild_kind,
1853
/* precomputed_pseudo_element_decls = */ None,
1854
)?;
1855
}
1856
1857
Ok(())
1858
}
1859
1860
/// Returns the invalidation map.
1861
pub fn invalidation_map(&self) -> &InvalidationMap {
1862
&self.invalidation_map
1863
}
1864
1865
/// Returns whether the given ElementState bit is relied upon by a selector
1866
/// of some rule.
1867
#[inline]
1868
pub fn has_state_dependency(&self, state: ElementState) -> bool {
1869
self.state_dependencies.intersects(state)
1870
}
1871
1872
/// Returns whether the given attribute might appear in an attribute
1873
/// selector of some rule.
1874
#[inline]
1875
pub fn might_have_attribute_dependency(&self, local_name: &LocalName) -> bool {
1876
self.attribute_dependencies.contains(local_name)
1877
}
1878
1879
/// Returns the normal rule map for a given pseudo-element.
1880
#[inline]
1881
pub fn normal_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
1882
self.normal_rules.rules(pseudo)
1883
}
1884
1885
/// Returns the host pseudo rule map for a given pseudo-element.
1886
#[inline]
1887
pub fn host_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
1888
self.host_rules.as_ref().and_then(|d| d.rules(pseudo))
1889
}
1890
1891
/// Returns the slotted rule map for a given pseudo-element.
1892
#[inline]
1893
pub fn slotted_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
1894
self.slotted_rules.as_ref().and_then(|d| d.rules(pseudo))
1895
}
1896
1897
/// Returns the parts rule map for a given pseudo-element.
1898
#[inline]
1899
pub fn part_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&PartMap> {
1900
self.part_rules.as_ref().and_then(|d| d.rules(pseudo))
1901
}
1902
1903
/// Collects all the applicable media query results into `results`.
1904
///
1905
/// This duplicates part of the logic in `add_stylesheet`, which is
1906
/// a bit unfortunate.
1907
///
1908
/// FIXME(emilio): With a bit of smartness in
1909
/// `media_feature_affected_matches`, we could convert
1910
/// `EffectiveMediaQueryResults` into a vector without too much effort.
1911
fn collect_applicable_media_query_results_into<S>(
1912
device: &Device,
1913
stylesheet: &S,
1914
guard: &SharedRwLockReadGuard,
1915
results: &mut EffectiveMediaQueryResults,
1916
) where
1917
S: StylesheetInDocument + ToMediaListKey + 'static,
1918
{
1919
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
1920
return;
1921
}
1922
1923
debug!(" + {:?}", stylesheet);
1924
results.saw_effective(stylesheet);
1925
1926
for rule in stylesheet.effective_rules(device, guard) {
1927
match *rule {
1928
CssRule::Import(ref lock) => {
1929
let import_rule = lock.read_with(guard);
1930
debug!(" + {:?}", import_rule.stylesheet.media(guard));
1931
results.saw_effective(import_rule);
1932
},
1933
CssRule::Media(ref lock) => {
1934
let media_rule = lock.read_with(guard);
1935
debug!(" + {:?}", media_rule.media_queries.read_with(guard));
1936
results.saw_effective(media_rule);
1937
},
1938
_ => {},
1939
}
1940
}
1941
}
1942
1943
// Returns Err(..) to signify OOM
1944
fn add_stylesheet<S>(
1945
&mut self,
1946
device: &Device,
1947
quirks_mode: QuirksMode,
1948
stylesheet: &S,
1949
guard: &SharedRwLockReadGuard,
1950
rebuild_kind: SheetRebuildKind,
1951
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
1952
) -> Result<(), FailedAllocationError>
1953
where
1954
S: StylesheetInDocument + ToMediaListKey + 'static,
1955
{
1956
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
1957
return Ok(());
1958
}
1959
1960
let origin = stylesheet.origin(guard);
1961
1962
if rebuild_kind.should_rebuild_invalidation() {
1963
self.effective_media_query_results.saw_effective(stylesheet);
1964
}
1965
1966
for rule in stylesheet.effective_rules(device, guard) {
1967
match *rule {
1968
CssRule::Style(ref locked) => {
1969
let style_rule = locked.read_with(&guard);
1970
self.num_declarations += style_rule.block.read_with(&guard).len();
1971
for selector in &style_rule.selectors.0 {
1972
self.num_selectors += 1;
1973
1974
let pseudo_element = selector.pseudo_element();
1975
1976
if let Some(pseudo) = pseudo_element {
1977
if pseudo.is_precomputed() {
1978
debug_assert!(selector.is_universal());
1979
debug_asser