Skip to main content

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, RuleCascadeFlags, 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    flags: RuleCascadeFlags,
44    layer_order: LayerOrder,
45}
46
47const_assert_eq!(
48    std::mem::size_of::<CascadePriority>(),
49    std::mem::size_of::<u32>()
50);
51
52impl PartialOrd for CascadePriority {
53    #[inline]
54    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
55        Some(self.cmp(other))
56    }
57}
58
59impl Ord for CascadePriority {
60    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
61        self.cascade_level.cmp(&other.cascade_level).then_with(|| {
62            let ordering = self.layer_order.cmp(&other.layer_order);
63            if ordering == std::cmp::Ordering::Equal {
64                return ordering;
65            }
66            // https://drafts.csswg.org/css-cascade-5/#cascade-layering
67            //
68            //     Cascade layers (like declarations) are ordered by order
69            //     of appearance. When comparing declarations that belong to
70            //     different layers, then for normal rules the declaration
71            //     whose cascade layer is last wins, and for important rules
72            //     the declaration whose cascade layer is first wins.
73            //
74            // But the style attribute layer for some reason is special.
75            if self.cascade_level.is_important()
76                && !self.layer_order.is_style_attribute_layer()
77                && !other.layer_order.is_style_attribute_layer()
78            {
79                ordering.reverse()
80            } else {
81                ordering
82            }
83        })
84    }
85}
86
87/// The kind of revert that we're applying.
88#[derive(Clone, Copy, Debug, Eq, PartialEq)]
89pub enum RevertKind {
90    /// revert
91    Origin,
92    /// revert-layer
93    Layer,
94    /// revert-rule
95    Rule,
96}
97
98impl CascadePriority {
99    /// Construct a new CascadePriority for a given (level, order, flags) triple.
100    pub fn new(
101        cascade_level: CascadeLevel,
102        layer_order: LayerOrder,
103        flags: RuleCascadeFlags,
104    ) -> Self {
105        Self {
106            cascade_level,
107            flags,
108            layer_order,
109        }
110    }
111
112    /// Returns the flags.
113    #[inline]
114    pub fn flags(&self) -> RuleCascadeFlags {
115        self.flags
116    }
117
118    /// Set given flags.
119    #[inline]
120    pub fn set_flags(&mut self, flags: RuleCascadeFlags) {
121        self.flags.insert(flags);
122    }
123
124    /// Returns the layer order.
125    #[inline]
126    pub fn layer_order(&self) -> LayerOrder {
127        self.layer_order
128    }
129
130    /// Returns the cascade level.
131    #[inline]
132    pub fn cascade_level(&self) -> CascadeLevel {
133        self.cascade_level
134    }
135
136    /// Whether this declaration should be allowed if `revert` / `revert-layer` / `revert-rule`
137    /// have been specified on a given origin.
138    ///
139    /// `self` is the priority at which the revert has been specified.
140    pub fn allows_when_reverted(&self, other: &Self, kind: RevertKind) -> bool {
141        match kind {
142            RevertKind::Origin => {
143                other.cascade_level.origin().origin() < self.cascade_level.origin().origin()
144            },
145            RevertKind::Layer => other.unimportant() < self.unimportant(),
146            // Any other declaration for the same property we apply in the cascade needs to come
147            // from another rule effectively.
148            RevertKind::Rule => true,
149        }
150    }
151
152    /// Convert this priority from "important" to "non-important", if needed.
153    pub fn unimportant(&self) -> Self {
154        Self {
155            cascade_level: self.cascade_level.unimportant(),
156            flags: self.flags,
157            layer_order: self.layer_order,
158        }
159    }
160
161    /// Convert this priority from "non-important" to "important", if needed.
162    pub fn important(&self) -> Self {
163        Self {
164            cascade_level: self.cascade_level.important(),
165            flags: self.flags,
166            layer_order: self.layer_order,
167        }
168    }
169
170    /// The same tree, in author origin, at the root layer.
171    pub fn same_tree_author_normal_at_root_layer() -> Self {
172        Self::new(
173            CascadeLevel::same_tree_author_normal(),
174            LayerOrder::root(),
175            RuleCascadeFlags::empty(),
176        )
177    }
178}
179
180/// Proximity to the scope root.
181///
182/// https://drafts.csswg.org/css-cascade-6/#cascade-proximity
183#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
184pub struct ScopeProximity(u16);
185
186impl PartialOrd for ScopeProximity {
187    #[inline]
188    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
189        Some(self.cmp(other))
190    }
191}
192
193impl Ord for ScopeProximity {
194    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
195        // Lower proximity to scope root wins
196        other.0.cmp(&self.0)
197    }
198}
199
200/// Sacrifice the largest possible value for infinity. This makes the comparison
201/// trivial.
202const PROXIMITY_INFINITY: u16 = u16::MAX;
203
204impl ScopeProximity {
205    /// Construct a new scope proximity.
206    pub fn new(proximity: usize) -> Self {
207        if cfg!(debug_assertions) && proximity >= PROXIMITY_INFINITY as usize {
208            warn!("Proximity out of bounds");
209        }
210        Self(proximity.clamp(0, (PROXIMITY_INFINITY - 1) as usize) as u16)
211    }
212
213    /// Create a scope proximity for delcarations outside of any scope root.
214    pub fn infinity() -> Self {
215        Self(PROXIMITY_INFINITY)
216    }
217
218    /// If the proximity is finite, get the value.
219    pub fn get(&self) -> Option<u16> {
220        (self.0 != PROXIMITY_INFINITY).then(|| self.0)
221    }
222}
223
224/// A property declaration together with its precedence among rules of equal
225/// specificity so that we can sort them.
226///
227/// This represents the declarations in a given declaration block for a given
228/// importance.
229#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
230pub struct ApplicableDeclarationBlock {
231    /// The style source, either a style rule, or a property declaration block.
232    #[ignore_malloc_size_of = "Arc"]
233    pub source: StyleSource,
234    /// Order of appearance in which this rule appears - Set to 0 if not relevant
235    /// (e.g. Declaration from `style="/*...*/"`, presentation hints, animations
236    /// - See `CascadePriority` instead).
237    source_order: u32,
238    /// The specificity of the selector.
239    pub specificity: u32,
240    /// The proximity to the scope root.
241    pub scope_proximity: ScopeProximity,
242    /// The cascade priority of the rule.
243    pub cascade_priority: CascadePriority,
244}
245
246impl ApplicableDeclarationBlock {
247    /// Constructs an applicable declaration block from a given property
248    /// declaration block and importance.
249    #[inline]
250    pub fn from_declarations(
251        declarations: Arc<Locked<PropertyDeclarationBlock>>,
252        level: CascadeLevel,
253        layer_order: LayerOrder,
254    ) -> Self {
255        ApplicableDeclarationBlock {
256            source: StyleSource::from_declarations(declarations),
257            source_order: 0,
258            specificity: 0,
259            scope_proximity: ScopeProximity::infinity(),
260            cascade_priority: CascadePriority::new(level, layer_order, RuleCascadeFlags::empty()),
261        }
262    }
263
264    /// Constructs an applicable declaration block from the given components.
265    #[inline]
266    pub fn new(
267        source: StyleSource,
268        source_order: u32,
269        level: CascadeLevel,
270        specificity: u32,
271        layer_order: LayerOrder,
272        scope_proximity: ScopeProximity,
273        flags: RuleCascadeFlags,
274    ) -> Self {
275        ApplicableDeclarationBlock {
276            source,
277            source_order: source_order & SOURCE_ORDER_MASK,
278            specificity,
279            scope_proximity,
280            cascade_priority: CascadePriority::new(level, layer_order, flags),
281        }
282    }
283
284    /// Returns the source order of the block.
285    #[inline]
286    pub fn source_order(&self) -> u32 {
287        self.source_order
288    }
289
290    /// Returns the cascade level of the block.
291    #[inline]
292    pub fn level(&self) -> CascadeLevel {
293        self.cascade_priority.cascade_level()
294    }
295
296    /// Returns the cascade level of the block.
297    #[inline]
298    pub fn layer_order(&self) -> LayerOrder {
299        self.cascade_priority.layer_order()
300    }
301
302    /// Returns the scope proximity of the block.
303    #[inline]
304    pub fn scope_proximity(&self) -> ScopeProximity {
305        self.scope_proximity
306    }
307
308    /// Convenience method to consume self and return the right thing for the
309    /// rule tree to iterate over.
310    #[inline]
311    pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) {
312        (self.source, self.cascade_priority)
313    }
314
315    /// Return the key used to sort applicable declarations.
316    #[inline]
317    pub fn sort_key(&self) -> (LayerOrder, u32, ScopeProximity, u32) {
318        (
319            self.layer_order(),
320            self.specificity,
321            self.scope_proximity(),
322            self.source_order(),
323        )
324    }
325}
326
327// Size of this struct determines sorting and selector-matching performance.
328size_of_test!(ApplicableDeclarationBlock, 24);