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