style/rule_tree/
mod.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#![deny(unsafe_code)]
6
7//! The rule tree.
8
9use crate::applicable_declarations::{ApplicableDeclarationList, CascadePriority};
10use crate::properties::{LonghandIdSet, PropertyDeclarationBlock};
11use crate::shared_lock::{Locked, StylesheetGuards};
12use crate::stylesheets::layer_rule::LayerOrder;
13use servo_arc::ArcBorrow;
14use smallvec::SmallVec;
15use std::io::{self, Write};
16
17mod core;
18pub mod level;
19mod map;
20mod source;
21mod unsafe_box;
22
23pub use self::core::{RuleTree, StrongRuleNode};
24pub use self::level::{CascadeLevel, CascadeOrigin, ShadowCascadeOrder};
25pub use self::source::StyleSource;
26
27bitflags! {
28    /// Flags that are part of the cascade priority, and that we use to track
29    /// information about where the rule came from.
30    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
31    pub struct RuleCascadeFlags: u8 {
32        /// Whether the rule is inside a @starting-style block.
33        const STARTING_STYLE = 1 << 0;
34        /// Whether the rule is inside an @appearance-base block.
35        const APPEARANCE_BASE = 1 << 1;
36    }
37}
38
39malloc_size_of::malloc_size_of_is_0!(RuleCascadeFlags);
40
41impl RuleTree {
42    fn dump<W: Write>(&self, guards: &StylesheetGuards, writer: &mut W) {
43        let _ = writeln!(writer, " + RuleTree");
44        self.root().dump(guards, writer, 0);
45    }
46
47    /// Dump the rule tree to stdout.
48    pub fn dump_stdout(&self, guards: &StylesheetGuards) {
49        let mut stdout = io::stdout();
50        self.dump(guards, &mut stdout);
51    }
52
53    /// Inserts the given rules, that must be in proper order by specifity, and
54    /// returns the corresponding rule node representing the last inserted one.
55    ///
56    /// !important rules are detected and inserted into the appropriate position
57    /// in the rule tree. This allows selector matching to ignore importance,
58    /// while still maintaining the appropriate cascade order in the rule tree.
59    pub fn insert_ordered_rules_with_important<'a, I>(
60        &self,
61        iter: I,
62        guards: &StylesheetGuards,
63    ) -> StrongRuleNode
64    where
65        I: Iterator<Item = (StyleSource, CascadePriority)>,
66    {
67        let mut current = self.root().clone();
68
69        let mut found_important = false;
70
71        let mut important_author = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
72        let mut important_user = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
73        let mut important_ua = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
74        let mut transition = None;
75
76        for (source, priority) in iter {
77            let level = priority.cascade_level();
78            debug_assert!(!level.is_important(), "Important levels handled internally");
79
80            let any_important = {
81                let pdb = source.read(level.guard(guards));
82                pdb.any_important()
83            };
84
85            if any_important {
86                found_important = true;
87                match level.origin() {
88                    CascadeOrigin::Author => {
89                        important_author.push((source.clone(), priority.important()))
90                    },
91                    CascadeOrigin::UA => important_ua.push((source.clone(), priority.important())),
92                    CascadeOrigin::User => {
93                        important_user.push((source.clone(), priority.important()))
94                    },
95                    _ => {},
96                };
97            }
98
99            // We don't optimize out empty rules, even though we could.
100            //
101            // Inspector relies on every rule being inserted in the normal level
102            // at least once, in order to return the rules with the correct
103            // specificity order.
104            //
105            // TODO(emilio): If we want to apply these optimizations without
106            // breaking inspector's expectations, we'd need to run
107            // selector-matching again at the inspector's request. That may or
108            // may not be a better trade-off.
109            if level.origin() == CascadeOrigin::Transitions && found_important {
110                // There can be at most one transition, and it will come at
111                // the end of the iterator. Stash it and apply it after
112                // !important rules.
113                debug_assert!(transition.is_none());
114                transition = Some(source);
115            } else {
116                current = current.ensure_child(self.root(), source, priority);
117            }
118        }
119
120        // Early-return in the common case of no !important declarations.
121        if !found_important {
122            return current;
123        }
124
125        // Insert important declarations, in order of increasing importance,
126        // followed by any transition rule.
127        //
128        // Important rules are sorted differently from unimportant ones by
129        // shadow order and cascade order.
130        if !important_author.is_empty()
131            && important_author.first().unwrap().1 != important_author.last().unwrap().1
132        {
133            // We only need to sort if the important rules come from
134            // different trees, but we need this sort to be stable.
135            //
136            // FIXME(emilio): This could maybe be smarter, probably by chunking
137            // the important rules while inserting, and iterating the outer
138            // chunks in reverse order.
139            //
140            // That is, if we have rules with levels like: -1 -1 -1 0 0 0 1 1 1,
141            // we're really only sorting the chunks, while keeping elements
142            // inside the same chunk already sorted. Seems like we could try to
143            // keep a SmallVec-of-SmallVecs with the chunks and just iterate the
144            // outer in reverse.
145            important_author.sort_by_key(|&(_, priority)| priority);
146        }
147
148        for (source, priority) in important_author.drain(..) {
149            current = current.ensure_child(self.root(), source, priority);
150        }
151
152        for (source, priority) in important_user.drain(..) {
153            current = current.ensure_child(self.root(), source, priority);
154        }
155
156        for (source, priority) in important_ua.drain(..) {
157            current = current.ensure_child(self.root(), source, priority);
158        }
159
160        if let Some(source) = transition {
161            current = current.ensure_child(
162                self.root(),
163                source,
164                CascadePriority::new(
165                    CascadeLevel::new(CascadeOrigin::Transitions),
166                    LayerOrder::root(),
167                    RuleCascadeFlags::empty(),
168                ),
169            );
170        }
171
172        current
173    }
174
175    /// Given a list of applicable declarations, insert the rules and return the
176    /// corresponding rule node.
177    pub fn compute_rule_node(
178        &self,
179        applicable_declarations: &mut ApplicableDeclarationList,
180        guards: &StylesheetGuards,
181    ) -> StrongRuleNode {
182        self.insert_ordered_rules_with_important(
183            applicable_declarations.drain(..).map(|d| d.for_rule_tree()),
184            guards,
185        )
186    }
187
188    /// Insert the given rules, that must be in proper order by specifity, and
189    /// return the corresponding rule node representing the last inserted one.
190    pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode
191    where
192        I: Iterator<Item = (StyleSource, CascadePriority)>,
193    {
194        self.insert_ordered_rules_from(self.root().clone(), iter)
195    }
196
197    fn insert_ordered_rules_from<'a, I>(&self, from: StrongRuleNode, iter: I) -> StrongRuleNode
198    where
199        I: Iterator<Item = (StyleSource, CascadePriority)>,
200    {
201        let mut current = from;
202        for (source, priority) in iter {
203            current = current.ensure_child(self.root(), source, priority);
204        }
205        current
206    }
207
208    /// Replaces a rule in a given level (if present) for another rule.
209    ///
210    /// Returns the resulting node that represents the new path, or None if
211    /// the old path is still valid.
212    pub fn update_rule_at_level(
213        &self,
214        level: CascadeLevel,
215        layer_order: LayerOrder,
216        pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
217        path: &StrongRuleNode,
218        guards: &StylesheetGuards,
219        important_rules_changed: &mut bool,
220    ) -> Option<StrongRuleNode> {
221        // TODO(emilio): Being smarter with lifetimes we could avoid a bit of
222        // the refcount churn.
223        let mut current = path.clone();
224        *important_rules_changed = false;
225
226        // First walk up until the first less-or-equally specific rule.
227        let mut children = SmallVec::<[_; 10]>::new();
228        while current.cascade_priority().cascade_level() > level {
229            children.push((
230                current.style_source().unwrap().clone(),
231                current.cascade_priority(),
232            ));
233            current = current.parent().unwrap().clone();
234        }
235
236        let cascade_priority = CascadePriority::new(level, layer_order, RuleCascadeFlags::empty());
237
238        // Then remove the one at the level we want to replace, if any.
239        //
240        // NOTE: Here we assume that only one rule can be at the level + priority we're replacing,
241        // which holds because we only use this for HTML style attribute, animations and transition
242        // rules.
243        //
244        // This is certainly true for HTML style attribute rules, animations, transitions, and
245        // SMIL.
246        if current.cascade_priority() == cascade_priority {
247            *important_rules_changed |= level.is_important();
248
249            let current_decls = current.style_source().unwrap().get();
250
251            // If the only rule at the level we're replacing is exactly the
252            // same as `pdb`, we're done, and `path` is still valid.
253            if let Some(ref pdb) = pdb {
254                // If the only rule at the level we're replacing is exactly the
255                // same as `pdb`, we're done, and `path` is still valid.
256                //
257                // TODO(emilio): Another potential optimization is the one where
258                // we can just replace the rule at that level for `pdb`, and
259                // then we don't need to re-create the children, and `path` is
260                // also equally valid. This is less likely, and would require an
261                // in-place mutation of the source, which is, at best, fiddly,
262                // so let's skip it for now.
263                let is_here_already = ArcBorrow::ptr_eq(pdb, &current_decls.borrow_arc());
264                if is_here_already {
265                    debug!("Picking the fast path in rule replacement");
266                    return None;
267                }
268            }
269
270            current = current.parent().unwrap().clone();
271        }
272
273        // Insert the rule if it's relevant at this level in the cascade.
274        //
275        // These optimizations are likely to be important, because the levels where replacements
276        // apply (style and animations) tend to trigger pretty bad styling cases already.
277        if let Some(pdb) = pdb {
278            if level.is_important() {
279                if pdb.read_with(level.guard(guards)).any_important() {
280                    current = current.ensure_child(
281                        self.root(),
282                        StyleSource::from_declarations(pdb.clone_arc()),
283                        cascade_priority,
284                    );
285                    *important_rules_changed = true;
286                }
287            } else {
288                if pdb.read_with(level.guard(guards)).any_normal() {
289                    current = current.ensure_child(
290                        self.root(),
291                        StyleSource::from_declarations(pdb.clone_arc()),
292                        cascade_priority,
293                    );
294                }
295            }
296        }
297
298        // Now the rule is in the relevant place, push the children as
299        // necessary.
300        let rule = self.insert_ordered_rules_from(current, children.drain(..).rev());
301        Some(rule)
302    }
303
304    /// Returns whether this rule node has any @starting-style rule.
305    pub fn has_starting_style(path: &StrongRuleNode) -> bool {
306        path.self_and_ancestors().any(|node| {
307            node.cascade_priority()
308                .flags()
309                .intersects(RuleCascadeFlags::STARTING_STYLE)
310        })
311    }
312
313    /// Returns new rule nodes without Transitions level rule.
314    pub fn remove_transition_rule_if_applicable(path: &StrongRuleNode) -> StrongRuleNode {
315        // Return a clone if there is no transition level.
316        if path.cascade_level().origin() != CascadeOrigin::Transitions {
317            return path.clone();
318        }
319
320        path.parent().unwrap().clone()
321    }
322
323    /// Returns new rule node without rules from declarative animations.
324    pub fn remove_animation_rules(&self, path: &StrongRuleNode) -> StrongRuleNode {
325        // Return a clone if there are no animation rules.
326        if !path.has_animation_or_transition_rules() {
327            return path.clone();
328        }
329
330        let iter = path.self_and_ancestors().take_while(|node| {
331            node.cascade_level() >= CascadeLevel::new(CascadeOrigin::SMILOverride)
332        });
333        let mut last = path;
334        let mut children = SmallVec::<[_; 10]>::new();
335        for node in iter {
336            if !node.cascade_level().is_animation() {
337                children.push((
338                    node.style_source().unwrap().clone(),
339                    node.cascade_priority(),
340                ));
341            }
342            last = node;
343        }
344
345        let rule = self
346            .insert_ordered_rules_from(last.parent().unwrap().clone(), children.drain(..).rev());
347        rule
348    }
349}
350
351impl StrongRuleNode {
352    /// Get an iterator for this rule node and its ancestors.
353    pub fn self_and_ancestors(&self) -> SelfAndAncestors<'_> {
354        SelfAndAncestors {
355            current: Some(self),
356        }
357    }
358
359    /// Returns true if there is either animation or transition level rule.
360    pub fn has_animation_or_transition_rules(&self) -> bool {
361        self.self_and_ancestors()
362            .take_while(|node| {
363                node.cascade_level() >= CascadeLevel::new(CascadeOrigin::SMILOverride)
364            })
365            .any(|node| node.cascade_level().is_animation())
366    }
367
368    /// Get a set of properties whose CascadeLevel are higher than Animations
369    /// but not equal to Transitions.
370    ///
371    /// If there are any custom properties, we set the boolean value of the
372    /// returned tuple to true.
373    pub fn get_properties_overriding_animations(
374        &self,
375        guards: &StylesheetGuards,
376    ) -> (LonghandIdSet, bool) {
377        use crate::properties::PropertyDeclarationId;
378
379        // We want to iterate over cascade levels that override the animations
380        // level, i.e.  !important levels and the transitions level.
381        //
382        // However, we actually want to skip the transitions level because
383        // although it is higher in the cascade than animations, when both
384        // transitions and animations are present for a given element and
385        // property, transitions are suppressed so that they don't actually
386        // override animations.
387        let iter = self
388            .self_and_ancestors()
389            .skip_while(|node| node.cascade_level().origin() == CascadeOrigin::Transitions)
390            .take_while(|node| node.cascade_level() > CascadeLevel::new(CascadeOrigin::Animations));
391        let mut result = (LonghandIdSet::new(), false);
392        for node in iter {
393            let style = node.style_source().unwrap();
394            for (decl, important) in style
395                .read(node.cascade_level().guard(guards))
396                .declaration_importance_iter()
397            {
398                // Although we are only iterating over cascade levels that
399                // override animations, in a given property declaration block we
400                // can have a mixture of !important and non-!important
401                // declarations but only the !important declarations actually
402                // override animations.
403                if important.important() {
404                    match decl.id() {
405                        PropertyDeclarationId::Longhand(id) => result.0.insert(id),
406                        PropertyDeclarationId::Custom(_) => result.1 = true,
407                    }
408                }
409            }
410        }
411        result
412    }
413}
414
415/// An iterator over a rule node and its ancestors.
416#[derive(Clone)]
417pub struct SelfAndAncestors<'a> {
418    current: Option<&'a StrongRuleNode>,
419}
420
421impl<'a> Iterator for SelfAndAncestors<'a> {
422    type Item = &'a StrongRuleNode;
423
424    fn next(&mut self) -> Option<Self::Item> {
425        self.current.map(|node| {
426            self.current = node.parent();
427            node
428        })
429    }
430}