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 //! High-level interface to CSS selector matching.
6 
7 #![allow(unsafe_code)]
8 #![deny(missing_docs)]
9 
10 use crate::computed_value_flags::ComputedValueFlags;
11 use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
12 use crate::context::{SharedStyleContext, StyleContext};
13 use crate::data::ElementData;
14 use crate::dom::TElement;
15 #[cfg(feature = "servo")]
16 use crate::dom::{OpaqueNode, TNode};
17 use crate::invalidation::element::restyle_hints::RestyleHint;
18 use crate::properties::longhands::display::computed_value::T as Display;
19 use crate::properties::ComputedValues;
20 use crate::rule_tree::{CascadeLevel, StrongRuleNode};
21 use crate::selector_parser::{PseudoElement, RestyleDamage};
22 use crate::style_resolver::ResolvedElementStyles;
23 use crate::traversal_flags::TraversalFlags;
24 use selectors::matching::ElementSelectorFlags;
25 use servo_arc::{Arc, ArcBorrow};
26 
27 /// Represents the result of comparing an element's old and new style.
28 #[derive(Debug)]
29 pub struct StyleDifference {
30     /// The resulting damage.
31     pub damage: RestyleDamage,
32 
33     /// Whether any styles changed.
34     pub change: StyleChange,
35 }
36 
37 /// Represents whether or not the style of an element has changed.
38 #[derive(Clone, Copy, Debug)]
39 pub enum StyleChange {
40     /// The style hasn't changed.
41     Unchanged,
42     /// The style has changed.
43     Changed {
44         /// Whether only reset structs changed.
45         reset_only: bool,
46     },
47 }
48 
49 /// Whether or not newly computed values for an element need to be cascade
50 /// to children.
51 #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
52 pub enum ChildCascadeRequirement {
53     /// Old and new computed values were the same, or we otherwise know that
54     /// we won't bother recomputing style for children, so we can skip cascading
55     /// the new values into child elements.
56     CanSkipCascade = 0,
57     /// The same as `MustCascadeChildren`, but we only need to actually
58     /// recascade if the child inherits any explicit reset style.
59     MustCascadeChildrenIfInheritResetStyle = 1,
60     /// Old and new computed values were different, so we must cascade the
61     /// new values to children.
62     MustCascadeChildren = 2,
63     /// The same as `MustCascadeChildren`, but for the entire subtree.  This is
64     /// used to handle root font-size updates needing to recascade the whole
65     /// document.
66     MustCascadeDescendants = 3,
67 }
68 
69 impl ChildCascadeRequirement {
70     /// Whether we can unconditionally skip the cascade.
can_skip_cascade(&self) -> bool71     pub fn can_skip_cascade(&self) -> bool {
72         matches!(*self, ChildCascadeRequirement::CanSkipCascade)
73     }
74 }
75 
76 /// Determines which styles are being cascaded currently.
77 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
78 enum CascadeVisitedMode {
79     /// Cascade the regular, unvisited styles.
80     Unvisited,
81     /// Cascade the styles used when an element's relevant link is visited.  A
82     /// "relevant link" is the element being matched if it is a link or the
83     /// nearest ancestor link.
84     Visited,
85 }
86 
87 trait PrivateMatchMethods: TElement {
88     /// Updates the rule nodes without re-running selector matching, using just
89     /// the rule tree, for a specific visited mode.
90     ///
91     /// Returns true if an !important rule was replaced.
replace_rules_internal( &self, replacements: RestyleHint, context: &mut StyleContext<Self>, cascade_visited: CascadeVisitedMode, cascade_inputs: &mut ElementCascadeInputs, ) -> bool92     fn replace_rules_internal(
93         &self,
94         replacements: RestyleHint,
95         context: &mut StyleContext<Self>,
96         cascade_visited: CascadeVisitedMode,
97         cascade_inputs: &mut ElementCascadeInputs,
98     ) -> bool {
99         use crate::properties::PropertyDeclarationBlock;
100         use crate::shared_lock::Locked;
101 
102         debug_assert!(
103             replacements.intersects(RestyleHint::replacements()) &&
104                 (replacements & !RestyleHint::replacements()).is_empty()
105         );
106 
107         let stylist = &context.shared.stylist;
108         let guards = &context.shared.guards;
109 
110         let primary_rules = match cascade_visited {
111             CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(),
112             CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(),
113         };
114 
115         let primary_rules = match primary_rules {
116             Some(r) => r,
117             None => return false,
118         };
119 
120         let replace_rule_node = |level: CascadeLevel,
121                                  pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
122                                  path: &mut StrongRuleNode|
123          -> bool {
124             let mut important_rules_changed = false;
125             let new_node = stylist.rule_tree().update_rule_at_level(
126                 level,
127                 pdb,
128                 path,
129                 guards,
130                 &mut important_rules_changed,
131             );
132             if let Some(n) = new_node {
133                 *path = n;
134             }
135             important_rules_changed
136         };
137 
138         if !context.shared.traversal_flags.for_animation_only() {
139             let mut result = false;
140             if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
141                 let style_attribute = self.style_attribute();
142                 result |= replace_rule_node(
143                     CascadeLevel::same_tree_author_normal(),
144                     style_attribute,
145                     primary_rules,
146                 );
147                 result |= replace_rule_node(
148                     CascadeLevel::same_tree_author_important(),
149                     style_attribute,
150                     primary_rules,
151                 );
152                 // FIXME(emilio): Still a hack!
153                 self.unset_dirty_style_attribute();
154             }
155             return result;
156         }
157 
158         // Animation restyle hints are processed prior to other restyle
159         // hints in the animation-only traversal.
160         //
161         // Non-animation restyle hints will be processed in a subsequent
162         // normal traversal.
163         if replacements.intersects(RestyleHint::for_animations()) {
164             debug_assert!(context.shared.traversal_flags.for_animation_only());
165 
166             if replacements.contains(RestyleHint::RESTYLE_SMIL) {
167                 replace_rule_node(
168                     CascadeLevel::SMILOverride,
169                     self.smil_override(),
170                     primary_rules,
171                 );
172             }
173 
174             if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
175                 replace_rule_node(
176                     CascadeLevel::Transitions,
177                     self.transition_rule().as_ref().map(|a| a.borrow_arc()),
178                     primary_rules,
179                 );
180             }
181 
182             if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
183                 replace_rule_node(
184                     CascadeLevel::Animations,
185                     self.animation_rule().as_ref().map(|a| a.borrow_arc()),
186                     primary_rules,
187                 );
188             }
189         }
190 
191         false
192     }
193 
194     /// If there is no transition rule in the ComputedValues, it returns None.
195     #[cfg(feature = "gecko")]
after_change_style( &self, context: &mut StyleContext<Self>, primary_style: &Arc<ComputedValues>, ) -> Option<Arc<ComputedValues>>196     fn after_change_style(
197         &self,
198         context: &mut StyleContext<Self>,
199         primary_style: &Arc<ComputedValues>,
200     ) -> Option<Arc<ComputedValues>> {
201         use crate::context::CascadeInputs;
202         use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
203         use crate::stylist::RuleInclusion;
204 
205         let rule_node = primary_style.rules();
206         let without_transition_rules = context
207             .shared
208             .stylist
209             .rule_tree()
210             .remove_transition_rule_if_applicable(rule_node);
211         if without_transition_rules == *rule_node {
212             // We don't have transition rule in this case, so return None to let
213             // the caller use the original ComputedValues.
214             return None;
215         }
216 
217         // FIXME(bug 868975): We probably need to transition visited style as
218         // well.
219         let inputs = CascadeInputs {
220             rules: Some(without_transition_rules),
221             visited_rules: primary_style.visited_rules().cloned(),
222         };
223 
224         // Actually `PseudoElementResolution` doesn't really matter.
225         let style = StyleResolverForElement::new(
226             *self,
227             context,
228             RuleInclusion::All,
229             PseudoElementResolution::IfApplicable,
230         )
231         .cascade_style_and_visited_with_default_parents(inputs);
232 
233         Some(style.0)
234     }
235 
236     #[cfg(feature = "gecko")]
needs_animations_update( &self, context: &mut StyleContext<Self>, old_style: Option<&ComputedValues>, new_style: &ComputedValues, ) -> bool237     fn needs_animations_update(
238         &self,
239         context: &mut StyleContext<Self>,
240         old_style: Option<&ComputedValues>,
241         new_style: &ComputedValues,
242     ) -> bool {
243         let new_box_style = new_style.get_box();
244         let new_style_specifies_animations = new_box_style.specifies_animations();
245 
246         let has_animations = self.has_css_animations();
247         if !new_style_specifies_animations && !has_animations {
248             return false;
249         }
250 
251         let old_style = match old_style {
252             Some(old) => old,
253             // If we have no old style but have animations, we may be a
254             // pseudo-element which was re-created without style changes.
255             //
256             // This can happen when we reframe the pseudo-element without
257             // restyling it (due to content insertion on a flex container or
258             // such, for example). See bug 1564366.
259             //
260             // FIXME(emilio): The really right fix for this is keeping the
261             // pseudo-element itself around on reframes, but that's a bit
262             // harder. If we do that we can probably remove quite a lot of the
263             // EffectSet complexity though, since right now it's stored on the
264             // parent element for pseudo-elements given we need to keep it
265             // around...
266             None => {
267                 return new_style_specifies_animations || new_style.is_pseudo_style();
268             },
269         };
270 
271         let old_box_style = old_style.get_box();
272 
273         let keyframes_could_have_changed = context
274             .shared
275             .traversal_flags
276             .contains(TraversalFlags::ForCSSRuleChanges);
277 
278         // If the traversal is triggered due to changes in CSS rules changes, we
279         // need to try to update all CSS animations on the element if the
280         // element has or will have CSS animation style regardless of whether
281         // the animation is running or not.
282         //
283         // TODO: We should check which @keyframes were added/changed/deleted and
284         // update only animations corresponding to those @keyframes.
285         if keyframes_could_have_changed {
286             return true;
287         }
288 
289         // If the animations changed, well...
290         if !old_box_style.animations_equals(new_box_style) {
291             return true;
292         }
293 
294         let old_display = old_box_style.clone_display();
295         let new_display = new_box_style.clone_display();
296 
297         // If we were display: none, we may need to trigger animations.
298         if old_display == Display::None && new_display != Display::None {
299             return new_style_specifies_animations;
300         }
301 
302         // If we are becoming display: none, we may need to stop animations.
303         if old_display != Display::None && new_display == Display::None {
304             return has_animations;
305         }
306 
307         // We might need to update animations if writing-mode or direction
308         // changed, and any of the animations contained logical properties.
309         //
310         // We may want to be more granular, but it's probably not worth it.
311         if new_style.writing_mode != old_style.writing_mode {
312             return has_animations;
313         }
314 
315         false
316     }
317 
318     /// Create a SequentialTask for resolving descendants in a SMIL display
319     /// property animation if the display property changed from none.
320     #[cfg(feature = "gecko")]
handle_display_change_for_smil_if_needed( &self, context: &mut StyleContext<Self>, old_values: Option<&ComputedValues>, new_values: &ComputedValues, restyle_hints: RestyleHint, )321     fn handle_display_change_for_smil_if_needed(
322         &self,
323         context: &mut StyleContext<Self>,
324         old_values: Option<&ComputedValues>,
325         new_values: &ComputedValues,
326         restyle_hints: RestyleHint,
327     ) {
328         use crate::context::PostAnimationTasks;
329 
330         if !restyle_hints.intersects(RestyleHint::RESTYLE_SMIL) {
331             return;
332         }
333 
334         if new_values.is_display_property_changed_from_none(old_values) {
335             // When display value is changed from none to other, we need to
336             // traverse descendant elements in a subsequent normal
337             // traversal (we can't traverse them in this animation-only restyle
338             // since we have no way to know whether the decendants
339             // need to be traversed at the beginning of the animation-only
340             // restyle).
341             let task = ::context::SequentialTask::process_post_animation(
342                 *self,
343                 PostAnimationTasks::DISPLAY_CHANGED_FROM_NONE_FOR_SMIL,
344             );
345             context.thread_local.tasks.push(task);
346         }
347     }
348 
349     #[cfg(feature = "gecko")]
process_animations( &self, context: &mut StyleContext<Self>, old_values: &mut Option<Arc<ComputedValues>>, new_values: &mut Arc<ComputedValues>, restyle_hint: RestyleHint, important_rules_changed: bool, )350     fn process_animations(
351         &self,
352         context: &mut StyleContext<Self>,
353         old_values: &mut Option<Arc<ComputedValues>>,
354         new_values: &mut Arc<ComputedValues>,
355         restyle_hint: RestyleHint,
356         important_rules_changed: bool,
357     ) {
358         use crate::context::UpdateAnimationsTasks;
359 
360         if context.shared.traversal_flags.for_animation_only() {
361             self.handle_display_change_for_smil_if_needed(
362                 context,
363                 old_values.as_ref().map(|v| &**v),
364                 new_values,
365                 restyle_hint,
366             );
367             return;
368         }
369 
370         // Bug 868975: These steps should examine and update the visited styles
371         // in addition to the unvisited styles.
372 
373         let mut tasks = UpdateAnimationsTasks::empty();
374         if self.needs_animations_update(context, old_values.as_ref().map(|s| &**s), new_values) {
375             tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
376         }
377 
378         let before_change_style = if self
379             .might_need_transitions_update(old_values.as_ref().map(|s| &**s), new_values)
380         {
381             let after_change_style = if self.has_css_transitions() {
382                 self.after_change_style(context, new_values)
383             } else {
384                 None
385             };
386 
387             // In order to avoid creating a SequentialTask for transitions which
388             // may not be updated, we check it per property to make sure Gecko
389             // side will really update transition.
390             let needs_transitions_update = {
391                 // We borrow new_values here, so need to add a scope to make
392                 // sure we release it before assigning a new value to it.
393                 let after_change_style_ref = after_change_style.as_ref().unwrap_or(&new_values);
394 
395                 self.needs_transitions_update(old_values.as_ref().unwrap(), after_change_style_ref)
396             };
397 
398             if needs_transitions_update {
399                 if let Some(values_without_transitions) = after_change_style {
400                     *new_values = values_without_transitions;
401                 }
402                 tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS);
403 
404                 // We need to clone old_values into SequentialTask, so we can
405                 // use it later.
406                 old_values.clone()
407             } else {
408                 None
409             }
410         } else {
411             None
412         };
413 
414         if self.has_animations() {
415             tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES);
416             if important_rules_changed {
417                 tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);
418             }
419             if new_values.is_display_property_changed_from_none(old_values.as_ref().map(|s| &**s)) {
420                 tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);
421             }
422         }
423 
424         if !tasks.is_empty() {
425             let task =
426                 ::context::SequentialTask::update_animations(*self, before_change_style, tasks);
427             context.thread_local.tasks.push(task);
428         }
429     }
430 
431     #[cfg(feature = "servo")]
process_animations( &self, context: &mut StyleContext<Self>, old_values: &mut Option<Arc<ComputedValues>>, new_values: &mut Arc<ComputedValues>, _restyle_hint: RestyleHint, _important_rules_changed: bool, )432     fn process_animations(
433         &self,
434         context: &mut StyleContext<Self>,
435         old_values: &mut Option<Arc<ComputedValues>>,
436         new_values: &mut Arc<ComputedValues>,
437         _restyle_hint: RestyleHint,
438         _important_rules_changed: bool,
439     ) {
440         use crate::animation;
441 
442         let this_opaque = self.as_node().opaque();
443         let mut expired_transitions = vec![];
444         let shared_context = context.shared;
445         if let Some(ref mut old_values) = *old_values {
446             // We apply the expired transitions and animations to the old style
447             // here, because we later compare the old style to the new style in
448             // `start_transitions_if_applicable`. If the styles differ then it will
449             // cause the expired transition to restart.
450             //
451             // TODO(mrobinson): We should really be following spec behavior and calculate
452             // after-change-style and before-change-style here.
453             Self::collect_and_update_style_for_expired_transitions(
454                 shared_context,
455                 this_opaque,
456                 old_values,
457                 &mut expired_transitions,
458             );
459 
460             Self::update_style_for_animations(
461                 shared_context,
462                 this_opaque,
463                 old_values,
464                 &context.thread_local.font_metrics_provider,
465             );
466         }
467 
468         let new_animations_sender = &context.thread_local.new_animations_sender;
469         // Trigger any present animations if necessary.
470         animation::maybe_start_animations(
471             *self,
472             &shared_context,
473             new_animations_sender,
474             this_opaque,
475             &new_values,
476         );
477 
478         // Trigger transitions if necessary. This will set `new_values` to
479         // the starting value of the transition if it did trigger a transition.
480         if let Some(ref values) = old_values {
481             animation::update_transitions(
482                 &shared_context,
483                 new_animations_sender,
484                 this_opaque,
485                 &values,
486                 new_values,
487                 &expired_transitions,
488             );
489         }
490     }
491 
492     /// Computes and applies non-redundant damage.
accumulate_damage_for( &self, shared_context: &SharedStyleContext, damage: &mut RestyleDamage, old_values: &ComputedValues, new_values: &ComputedValues, pseudo: Option<&PseudoElement>, ) -> ChildCascadeRequirement493     fn accumulate_damage_for(
494         &self,
495         shared_context: &SharedStyleContext,
496         damage: &mut RestyleDamage,
497         old_values: &ComputedValues,
498         new_values: &ComputedValues,
499         pseudo: Option<&PseudoElement>,
500     ) -> ChildCascadeRequirement {
501         debug!("accumulate_damage_for: {:?}", self);
502         debug_assert!(!shared_context
503             .traversal_flags
504             .contains(TraversalFlags::FinalAnimationTraversal));
505 
506         let difference = self.compute_style_difference(old_values, new_values, pseudo);
507 
508         *damage |= difference.damage;
509 
510         debug!(" > style difference: {:?}", difference);
511 
512         // We need to cascade the children in order to ensure the correct
513         // propagation of inherited computed value flags.
514         if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() {
515             debug!(
516                 " > flags changed: {:?} != {:?}",
517                 old_values.flags, new_values.flags
518             );
519             return ChildCascadeRequirement::MustCascadeChildren;
520         }
521 
522         match difference.change {
523             StyleChange::Unchanged => return ChildCascadeRequirement::CanSkipCascade,
524             StyleChange::Changed { reset_only } => {
525                 // If inherited properties changed, the best we can do is
526                 // cascade the children.
527                 if !reset_only {
528                     return ChildCascadeRequirement::MustCascadeChildren;
529                 }
530             },
531         }
532 
533         let old_display = old_values.get_box().clone_display();
534         let new_display = new_values.get_box().clone_display();
535 
536         if old_display != new_display {
537             // If we used to be a display: none element, and no longer are, our
538             // children need to be restyled because they're unstyled.
539             if old_display == Display::None {
540                 return ChildCascadeRequirement::MustCascadeChildren;
541             }
542             // Blockification of children may depend on our display value,
543             // so we need to actually do the recascade. We could potentially
544             // do better, but it doesn't seem worth it.
545             if old_display.is_item_container() != new_display.is_item_container() {
546                 return ChildCascadeRequirement::MustCascadeChildren;
547             }
548             // We may also need to blockify and un-blockify descendants if our
549             // display goes from / to display: contents, since the "layout
550             // parent style" changes.
551             if old_display.is_contents() || new_display.is_contents() {
552                 return ChildCascadeRequirement::MustCascadeChildren;
553             }
554             // Line break suppression may also be affected if the display
555             // type changes from ruby to non-ruby.
556             #[cfg(feature = "gecko")]
557             {
558                 if old_display.is_ruby_type() != new_display.is_ruby_type() {
559                     return ChildCascadeRequirement::MustCascadeChildren;
560                 }
561             }
562         }
563 
564         // Children with justify-items: auto may depend on our
565         // justify-items property value.
566         //
567         // Similarly, we could potentially do better, but this really
568         // seems not common enough to care about.
569         #[cfg(feature = "gecko")]
570         {
571             use crate::values::specified::align::AlignFlags;
572 
573             let old_justify_items = old_values.get_position().clone_justify_items();
574             let new_justify_items = new_values.get_position().clone_justify_items();
575 
576             let was_legacy_justify_items =
577                 old_justify_items.computed.0.contains(AlignFlags::LEGACY);
578 
579             let is_legacy_justify_items = new_justify_items.computed.0.contains(AlignFlags::LEGACY);
580 
581             if is_legacy_justify_items != was_legacy_justify_items {
582                 return ChildCascadeRequirement::MustCascadeChildren;
583             }
584 
585             if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed
586             {
587                 return ChildCascadeRequirement::MustCascadeChildren;
588             }
589         }
590 
591         #[cfg(feature = "servo")]
592         {
593             // We may need to set or propagate the CAN_BE_FRAGMENTED bit
594             // on our children.
595             if old_values.is_multicol() != new_values.is_multicol() {
596                 return ChildCascadeRequirement::MustCascadeChildren;
597             }
598         }
599 
600         // We could prove that, if our children don't inherit reset
601         // properties, we can stop the cascade.
602         ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle
603     }
604 
605     #[cfg(feature = "servo")]
collect_and_update_style_for_expired_transitions( context: &SharedStyleContext, node: OpaqueNode, style: &mut Arc<ComputedValues>, expired_transitions: &mut Vec<crate::animation::PropertyAnimation>, )606     fn collect_and_update_style_for_expired_transitions(
607         context: &SharedStyleContext,
608         node: OpaqueNode,
609         style: &mut Arc<ComputedValues>,
610         expired_transitions: &mut Vec<crate::animation::PropertyAnimation>,
611     ) {
612         use crate::animation::Animation;
613 
614         let mut all_expired_animations = context.expired_animations.write();
615         if let Some(animations) = all_expired_animations.remove(&node) {
616             debug!("removing expired animations for {:?}", node);
617             for animation in animations {
618                 debug!("Updating expired animation {:?}", animation);
619                 // TODO: support animation-fill-mode
620                 if let Animation::Transition(_, _, property_animation) = animation {
621                     property_animation.update(Arc::make_mut(style), 1.0);
622                     expired_transitions.push(property_animation);
623                 }
624             }
625         }
626     }
627 
628     #[cfg(feature = "servo")]
update_style_for_animations( context: &SharedStyleContext, node: OpaqueNode, style: &mut Arc<ComputedValues>, font_metrics: &dyn crate::font_metrics::FontMetricsProvider, )629     fn update_style_for_animations(
630         context: &SharedStyleContext,
631         node: OpaqueNode,
632         style: &mut Arc<ComputedValues>,
633         font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
634     ) {
635         use crate::animation::{self, Animation, AnimationUpdate};
636 
637         let mut all_running_animations = context.running_animations.write();
638         let running_animations = match all_running_animations.get_mut(&node) {
639             Some(running_animations) => running_animations,
640             None => return,
641         };
642 
643         for running_animation in running_animations.iter_mut() {
644             let update = match *running_animation {
645                 Animation::Transition(..) => continue,
646                 Animation::Keyframes(..) => animation::update_style_for_animation::<Self>(
647                     context,
648                     running_animation,
649                     style,
650                     font_metrics,
651                 ),
652             };
653 
654             match *running_animation {
655                 Animation::Transition(..) => unreachable!(),
656                 Animation::Keyframes(_, _, _, ref mut state) => match update {
657                     AnimationUpdate::Regular => {},
658                     AnimationUpdate::AnimationCanceled => {
659                         state.expired = true;
660                     },
661                 },
662             }
663         }
664     }
665 }
666 
667 impl<E: TElement> PrivateMatchMethods for E {}
668 
669 /// The public API that elements expose for selector matching.
670 pub trait MatchMethods: TElement {
671     /// Returns the closest parent element that doesn't have a display: contents
672     /// style (and thus generates a box).
673     ///
674     /// This is needed to correctly handle blockification of flex and grid
675     /// items.
676     ///
677     /// Returns itself if the element has no parent. In practice this doesn't
678     /// happen because the root element is blockified per spec, but it could
679     /// happen if we decide to not blockify for roots of disconnected subtrees,
680     /// which is a kind of dubious behavior.
layout_parent(&self) -> Self681     fn layout_parent(&self) -> Self {
682         let mut current = self.clone();
683         loop {
684             current = match current.traversal_parent() {
685                 Some(el) => el,
686                 None => return current,
687             };
688 
689             let is_display_contents = current
690                 .borrow_data()
691                 .unwrap()
692                 .styles
693                 .primary()
694                 .is_display_contents();
695 
696             if !is_display_contents {
697                 return current;
698             }
699         }
700     }
701 
702     /// Updates the styles with the new ones, diffs them, and stores the restyle
703     /// damage.
finish_restyle( &self, context: &mut StyleContext<Self>, data: &mut ElementData, mut new_styles: ResolvedElementStyles, important_rules_changed: bool, ) -> ChildCascadeRequirement704     fn finish_restyle(
705         &self,
706         context: &mut StyleContext<Self>,
707         data: &mut ElementData,
708         mut new_styles: ResolvedElementStyles,
709         important_rules_changed: bool,
710     ) -> ChildCascadeRequirement {
711         use std::cmp;
712 
713         self.process_animations(
714             context,
715             &mut data.styles.primary,
716             &mut new_styles.primary.style.0,
717             data.hint,
718             important_rules_changed,
719         );
720 
721         // First of all, update the styles.
722         let old_styles = data.set_styles(new_styles);
723 
724         let new_primary_style = data.styles.primary.as_ref().unwrap();
725 
726         let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade;
727         if new_primary_style
728             .flags
729             .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE)
730         {
731             let device = context.shared.stylist.device();
732             let new_font_size = new_primary_style.get_font().clone_font_size();
733 
734             if old_styles
735                 .primary
736                 .as_ref()
737                 .map_or(true, |s| s.get_font().clone_font_size() != new_font_size)
738             {
739                 debug_assert!(self.owner_doc_matches_for_testing(device));
740                 device.set_root_font_size(new_font_size.size().into());
741                 // If the root font-size changed since last time, and something
742                 // in the document did use rem units, ensure we recascade the
743                 // entire tree.
744                 if device.used_root_font_size() {
745                     cascade_requirement = ChildCascadeRequirement::MustCascadeDescendants;
746                 }
747             }
748         }
749 
750         if context.shared.stylist.quirks_mode() == QuirksMode::Quirks {
751             if self.is_html_document_body_element() {
752                 // NOTE(emilio): We _could_ handle dynamic changes to it if it
753                 // changes and before we reach our children the cascade stops,
754                 // but we don't track right now whether we use the document body
755                 // color, and nobody else handles that properly anyway.
756 
757                 let device = context.shared.stylist.device();
758 
759                 // Needed for the "inherit from body" quirk.
760                 let text_color = new_primary_style.get_inherited_text().clone_color();
761                 device.set_body_text_color(text_color);
762             }
763         }
764 
765         // Don't accumulate damage if we're in the final animation traversal.
766         if context
767             .shared
768             .traversal_flags
769             .contains(TraversalFlags::FinalAnimationTraversal)
770         {
771             return ChildCascadeRequirement::MustCascadeChildren;
772         }
773 
774         // Also, don't do anything if there was no style.
775         let old_primary_style = match old_styles.primary {
776             Some(s) => s,
777             None => return ChildCascadeRequirement::MustCascadeChildren,
778         };
779 
780         cascade_requirement = cmp::max(
781             cascade_requirement,
782             self.accumulate_damage_for(
783                 context.shared,
784                 &mut data.damage,
785                 &old_primary_style,
786                 new_primary_style,
787                 None,
788             ),
789         );
790 
791         if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
792             // This is the common case; no need to examine pseudos here.
793             return cascade_requirement;
794         }
795 
796         let pseudo_styles = old_styles
797             .pseudos
798             .as_array()
799             .iter()
800             .zip(data.styles.pseudos.as_array().iter());
801 
802         for (i, (old, new)) in pseudo_styles.enumerate() {
803             match (old, new) {
804                 (&Some(ref old), &Some(ref new)) => {
805                     self.accumulate_damage_for(
806                         context.shared,
807                         &mut data.damage,
808                         old,
809                         new,
810                         Some(&PseudoElement::from_eager_index(i)),
811                     );
812                 },
813                 (&None, &None) => {},
814                 _ => {
815                     // It's possible that we're switching from not having
816                     // ::before/::after at all to having styles for them but not
817                     // actually having a useful pseudo-element.  Check for that
818                     // case.
819                     let pseudo = PseudoElement::from_eager_index(i);
820                     let new_pseudo_should_exist =
821                         new.as_ref().map_or(false, |s| pseudo.should_exist(s));
822                     let old_pseudo_should_exist =
823                         old.as_ref().map_or(false, |s| pseudo.should_exist(s));
824                     if new_pseudo_should_exist != old_pseudo_should_exist {
825                         data.damage |= RestyleDamage::reconstruct();
826                         return cascade_requirement;
827                     }
828                 },
829             }
830         }
831 
832         cascade_requirement
833     }
834 
835     /// Applies selector flags to an element, deferring mutations of the parent
836     /// until after the traversal.
837     ///
838     /// TODO(emilio): This is somewhat inefficient, because it doesn't take
839     /// advantage of us knowing that the traversal is sequential.
apply_selector_flags( &self, map: &mut SelectorFlagsMap<Self>, element: &Self, flags: ElementSelectorFlags, )840     fn apply_selector_flags(
841         &self,
842         map: &mut SelectorFlagsMap<Self>,
843         element: &Self,
844         flags: ElementSelectorFlags,
845     ) {
846         // Handle flags that apply to the element.
847         let self_flags = flags.for_self();
848         if !self_flags.is_empty() {
849             if element == self {
850                 // If this is the element we're styling, we have exclusive
851                 // access to the element, and thus it's fine inserting them,
852                 // even from the worker.
853                 unsafe {
854                     element.set_selector_flags(self_flags);
855                 }
856             } else {
857                 // Otherwise, this element is an ancestor of the current element
858                 // we're styling, and thus multiple children could write to it
859                 // if we did from here.
860                 //
861                 // Instead, we can read them, and post them if necessary as a
862                 // sequential task in order for them to be processed later.
863                 if !element.has_selector_flags(self_flags) {
864                     map.insert_flags(*element, self_flags);
865                 }
866             }
867         }
868 
869         // Handle flags that apply to the parent.
870         let parent_flags = flags.for_parent();
871         if !parent_flags.is_empty() {
872             if let Some(p) = element.parent_element() {
873                 if !p.has_selector_flags(parent_flags) {
874                     map.insert_flags(p, parent_flags);
875                 }
876             }
877         }
878     }
879 
880     /// Updates the rule nodes without re-running selector matching, using just
881     /// the rule tree.
882     ///
883     /// Returns true if an !important rule was replaced.
replace_rules( &self, replacements: RestyleHint, context: &mut StyleContext<Self>, cascade_inputs: &mut ElementCascadeInputs, ) -> bool884     fn replace_rules(
885         &self,
886         replacements: RestyleHint,
887         context: &mut StyleContext<Self>,
888         cascade_inputs: &mut ElementCascadeInputs,
889     ) -> bool {
890         let mut result = false;
891         result |= self.replace_rules_internal(
892             replacements,
893             context,
894             CascadeVisitedMode::Unvisited,
895             cascade_inputs,
896         );
897         result |= self.replace_rules_internal(
898             replacements,
899             context,
900             CascadeVisitedMode::Visited,
901             cascade_inputs,
902         );
903         result
904     }
905 
906     /// Given the old and new style of this element, and whether it's a
907     /// pseudo-element, compute the restyle damage used to determine which
908     /// kind of layout or painting operations we'll need.
compute_style_difference( &self, old_values: &ComputedValues, new_values: &ComputedValues, pseudo: Option<&PseudoElement>, ) -> StyleDifference909     fn compute_style_difference(
910         &self,
911         old_values: &ComputedValues,
912         new_values: &ComputedValues,
913         pseudo: Option<&PseudoElement>,
914     ) -> StyleDifference {
915         debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
916         RestyleDamage::compute_style_difference(old_values, new_values)
917     }
918 }
919 
920 impl<E: TElement> MatchMethods for E {}
921