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