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