style/
applicable_declarations.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Applicable declarations management.
6
7use crate::derives::*;
8use crate::properties::PropertyDeclarationBlock;
9use crate::rule_tree::{CascadeLevel, StyleSource};
10use crate::shared_lock::Locked;
11use crate::stylesheets::layer_rule::LayerOrder;
12use servo_arc::Arc;
13use smallvec::SmallVec;
14
15/// List of applicable declarations. This is a transient structure that shuttles
16/// declarations between selector matching and inserting into the rule tree, and
17/// therefore we want to avoid heap-allocation where possible.
18///
19/// In measurements on wikipedia, we pretty much never have more than 8 applicable
20/// declarations, so we could consider making this 8 entries instead of 16.
21/// However, it may depend a lot on workload, and stack space is cheap.
22pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;
23
24/// Blink uses 18 bits to store source order, and does not check overflow [1].
25/// That's a limit that could be reached in realistic webpages, so we use
26/// 24 bits and enforce defined behavior in the overflow case.
27///
28/// Note that right now this restriction could be lifted if wanted (because we
29/// no longer stash the cascade level in the remaining bits), but we keep it in
30/// place in case we come up with a use-case for them, lacking reports of the
31/// current limit being too small.
32///
33/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/
34///     RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
35const SOURCE_ORDER_BITS: usize = 24;
36const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
37const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX;
38
39/// The cascade-level+layer order of this declaration.
40#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
41pub struct CascadePriority {
42    cascade_level: CascadeLevel,
43    layer_order: LayerOrder,
44}
45
46const_assert_eq!(
47    std::mem::size_of::<CascadePriority>(),
48    std::mem::size_of::<u32>()
49);
50
51impl PartialOrd for CascadePriority {
52    #[inline]
53    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
54        Some(self.cmp(other))
55    }
56}
57
58impl Ord for CascadePriority {
59    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
60        self.cascade_level.cmp(&other.cascade_level).then_with(|| {
61            let ordering = self.layer_order.cmp(&other.layer_order);
62            if ordering == std::cmp::Ordering::Equal {
63                return ordering;
64            }
65            // https://drafts.csswg.org/css-cascade-5/#cascade-layering
66            //
67            //     Cascade layers (like declarations) are ordered by order
68            //     of appearance. When comparing declarations that belong to
69            //     different layers, then for normal rules the declaration
70            //     whose cascade layer is last wins, and for important rules
71            //     the declaration whose cascade layer is first wins.
72            //
73            // But the style attribute layer for some reason is special.
74            if self.cascade_level.is_important()
75                && !self.layer_order.is_style_attribute_layer()
76                && !other.layer_order.is_style_attribute_layer()
77            {
78                ordering.reverse()
79            } else {
80                ordering
81            }
82        })
83    }
84}
85
86impl CascadePriority {
87    /// Construct a new CascadePriority for a given (level, order) pair.
88    pub fn new(cascade_level: CascadeLevel, layer_order: LayerOrder) -> Self {
89        Self {
90            cascade_level,
91            layer_order,
92        }
93    }
94
95    /// Returns the layer order.
96    #[inline]
97    pub fn layer_order(&self) -> LayerOrder {
98        self.layer_order
99    }
100
101    /// Returns the cascade level.
102    #[inline]
103    pub fn cascade_level(&self) -> CascadeLevel {
104        self.cascade_level
105    }
106
107    /// Whether this declaration should be allowed if `revert` or `revert-layer`
108    /// have been specified on a given origin.
109    ///
110    /// `self` is the priority at which the `revert` or `revert-layer` keyword
111    /// have been specified.
112    pub fn allows_when_reverted(&self, other: &Self, origin_revert: bool) -> bool {
113        if origin_revert {
114            other.cascade_level.origin() < self.cascade_level.origin()
115        } else {
116            other.unimportant() < self.unimportant()
117        }
118    }
119
120    /// Convert this priority from "important" to "non-important", if needed.
121    pub fn unimportant(&self) -> Self {
122        Self::new(self.cascade_level().unimportant(), self.layer_order())
123    }
124
125    /// Convert this priority from "non-important" to "important", if needed.
126    pub fn important(&self) -> Self {
127        Self::new(self.cascade_level().important(), self.layer_order())
128    }
129
130    /// The same tree, in author origin, at the root layer.
131    pub fn same_tree_author_normal_at_root_layer() -> Self {
132        Self::new(CascadeLevel::same_tree_author_normal(), LayerOrder::root())
133    }
134}
135
136/// Proximity to the scope root.
137///
138/// https://drafts.csswg.org/css-cascade-6/#cascade-proximity
139#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
140pub struct ScopeProximity(u16);
141
142impl PartialOrd for ScopeProximity {
143    #[inline]
144    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
145        Some(self.cmp(other))
146    }
147}
148
149impl Ord for ScopeProximity {
150    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
151        // Lower proximity to scope root wins
152        other.0.cmp(&self.0)
153    }
154}
155
156/// Sacrifice the largest possible value for infinity. This makes the comparison
157/// trivial.
158const PROXIMITY_INFINITY: u16 = u16::MAX;
159
160impl ScopeProximity {
161    /// Construct a new scope proximity.
162    pub fn new(proximity: usize) -> Self {
163        if cfg!(debug_assertions) && proximity >= PROXIMITY_INFINITY as usize {
164            warn!("Proximity out of bounds");
165        }
166        Self(proximity.clamp(0, (PROXIMITY_INFINITY - 1) as usize) as u16)
167    }
168
169    /// Create a scope proximity for delcarations outside of any scope root.
170    pub fn infinity() -> Self {
171        Self(PROXIMITY_INFINITY)
172    }
173
174    /// If the proximity is finite, get the value.
175    pub fn get(&self) -> Option<u16> {
176        (self.0 != PROXIMITY_INFINITY).then(|| self.0)
177    }
178}
179
180/// A property declaration together with its precedence among rules of equal
181/// specificity so that we can sort them.
182///
183/// This represents the declarations in a given declaration block for a given
184/// importance.
185#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
186pub struct ApplicableDeclarationBlock {
187    /// The style source, either a style rule, or a property declaration block.
188    #[ignore_malloc_size_of = "Arc"]
189    pub source: StyleSource,
190    /// Order of appearance in which this rule appears - Set to 0 if not relevant
191    /// (e.g. Declaration from `style="/*...*/"`, presentation hints, animations
192    /// - See `CascadePriority` instead).
193    source_order: u32,
194    /// The specificity of the selector.
195    pub specificity: u32,
196    /// The proximity to the scope root.
197    pub scope_proximity: ScopeProximity,
198    /// The cascade priority of the rule.
199    pub cascade_priority: CascadePriority,
200}
201
202impl ApplicableDeclarationBlock {
203    /// Constructs an applicable declaration block from a given property
204    /// declaration block and importance.
205    #[inline]
206    pub fn from_declarations(
207        declarations: Arc<Locked<PropertyDeclarationBlock>>,
208        level: CascadeLevel,
209        layer_order: LayerOrder,
210    ) -> Self {
211        ApplicableDeclarationBlock {
212            source: StyleSource::from_declarations(declarations),
213            source_order: 0,
214            specificity: 0,
215            scope_proximity: ScopeProximity::infinity(),
216            cascade_priority: CascadePriority::new(level, layer_order),
217        }
218    }
219
220    /// Constructs an applicable declaration block from the given components.
221    #[inline]
222    pub fn new(
223        source: StyleSource,
224        source_order: u32,
225        level: CascadeLevel,
226        specificity: u32,
227        layer_order: LayerOrder,
228        scope_proximity: ScopeProximity,
229    ) -> Self {
230        ApplicableDeclarationBlock {
231            source,
232            source_order: source_order & SOURCE_ORDER_MASK,
233            specificity,
234            scope_proximity,
235            cascade_priority: CascadePriority::new(level, layer_order),
236        }
237    }
238
239    /// Returns the source order of the block.
240    #[inline]
241    pub fn source_order(&self) -> u32 {
242        self.source_order
243    }
244
245    /// Returns the cascade level of the block.
246    #[inline]
247    pub fn level(&self) -> CascadeLevel {
248        self.cascade_priority.cascade_level()
249    }
250
251    /// Returns the cascade level of the block.
252    #[inline]
253    pub fn layer_order(&self) -> LayerOrder {
254        self.cascade_priority.layer_order()
255    }
256
257    /// Returns the scope proximity of the block.
258    #[inline]
259    pub fn scope_proximity(&self) -> ScopeProximity {
260        self.scope_proximity
261    }
262
263    /// Convenience method to consume self and return the right thing for the
264    /// rule tree to iterate over.
265    #[inline]
266    pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) {
267        (self.source, self.cascade_priority)
268    }
269
270    /// Return the key used to sort applicable declarations.
271    #[inline]
272    pub fn sort_key(&self) -> (LayerOrder, u32, ScopeProximity, u32) {
273        (
274            self.layer_order(),
275            self.specificity,
276            self.scope_proximity(),
277            self.source_order(),
278        )
279    }
280}
281
282// Size of this struct determines sorting and selector-matching performance.
283size_of_test!(ApplicableDeclarationBlock, 24);