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, ¤t_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}