1#![allow(unsafe_code)]
8#![deny(missing_docs)]
9
10use crate::computed_value_flags::ComputedValueFlags;
11#[cfg(feature = "servo")]
12use crate::context::CascadeInputs;
13use crate::context::{ElementCascadeInputs, QuirksMode};
14use crate::context::{SharedStyleContext, StyleContext};
15use crate::data::{ElementData, ElementStyles};
16use crate::dom::TElement;
17#[cfg(feature = "servo")]
18use crate::dom::TNode;
19use crate::invalidation::element::restyle_hints::RestyleHint;
20use crate::properties::longhands::display::computed_value::T as Display;
21use crate::properties::ComputedValues;
22use crate::properties::PropertyDeclarationBlock;
23use crate::rule_tree::{CascadeLevel, StrongRuleNode};
24use crate::selector_parser::{PseudoElement, RestyleDamage};
25use crate::shared_lock::Locked;
26use crate::style_resolver::StyleResolverForElement;
27use crate::style_resolver::{PseudoElementResolution, ResolvedElementStyles};
28use crate::stylesheets::layer_rule::LayerOrder;
29use crate::stylist::RuleInclusion;
30use crate::traversal_flags::TraversalFlags;
31use servo_arc::{Arc, ArcBorrow};
32
33#[derive(Debug)]
35pub struct StyleDifference {
36 pub damage: RestyleDamage,
38
39 pub change: StyleChange,
41}
42
43#[derive(Clone, Copy, Debug)]
45pub enum StyleChange {
46 Unchanged,
48 Changed {
50 reset_only: bool,
52 },
53}
54
55#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
59pub enum ChildRestyleRequirement {
60 CanSkipCascade = 0,
64 MustCascadeChildrenIfInheritResetStyle = 1,
67 MustCascadeChildren = 2,
70 MustCascadeDescendants = 3,
74 MustMatchDescendants = 4,
78}
79
80#[derive(Clone, Copy, Debug, Eq, PartialEq)]
82enum CascadeVisitedMode {
83 Unvisited,
85 Visited,
89}
90
91trait PrivateMatchMethods: TElement {
92 fn replace_single_rule_node(
93 context: &SharedStyleContext,
94 level: CascadeLevel,
95 layer_order: LayerOrder,
96 pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
97 path: &mut StrongRuleNode,
98 ) -> bool {
99 let stylist = &context.stylist;
100 let guards = &context.guards;
101
102 let mut important_rules_changed = false;
103 let new_node = stylist.rule_tree().update_rule_at_level(
104 level,
105 layer_order,
106 pdb,
107 path,
108 guards,
109 &mut important_rules_changed,
110 );
111 if let Some(n) = new_node {
112 *path = n;
113 }
114 important_rules_changed
115 }
116
117 fn replace_rules_internal(
122 &self,
123 replacements: RestyleHint,
124 context: &mut StyleContext<Self>,
125 cascade_visited: CascadeVisitedMode,
126 cascade_inputs: &mut ElementCascadeInputs,
127 ) -> bool {
128 debug_assert!(
129 replacements.intersects(RestyleHint::replacements())
130 && (replacements & !RestyleHint::replacements()).is_empty()
131 );
132
133 let primary_rules = match cascade_visited {
134 CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(),
135 CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(),
136 };
137
138 let primary_rules = match primary_rules {
139 Some(r) => r,
140 None => return false,
141 };
142
143 if !context.shared.traversal_flags.for_animation_only() {
144 let mut result = false;
145 if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
146 let style_attribute = self.style_attribute();
147 result |= Self::replace_single_rule_node(
148 context.shared,
149 CascadeLevel::same_tree_author_normal(),
150 LayerOrder::style_attribute(),
151 style_attribute,
152 primary_rules,
153 );
154 result |= Self::replace_single_rule_node(
155 context.shared,
156 CascadeLevel::same_tree_author_important(),
157 LayerOrder::style_attribute(),
158 style_attribute,
159 primary_rules,
160 );
161 self.unset_dirty_style_attribute();
163 }
164 return result;
165 }
166
167 if replacements.intersects(RestyleHint::for_animations()) {
173 debug_assert!(context.shared.traversal_flags.for_animation_only());
174
175 if replacements.contains(RestyleHint::RESTYLE_SMIL) {
176 Self::replace_single_rule_node(
177 context.shared,
178 CascadeLevel::SMILOverride,
179 LayerOrder::root(),
180 self.smil_override(),
181 primary_rules,
182 );
183 }
184
185 if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
186 Self::replace_single_rule_node(
187 context.shared,
188 CascadeLevel::Transitions,
189 LayerOrder::root(),
190 self.transition_rule(&context.shared)
191 .as_ref()
192 .map(|a| a.borrow_arc()),
193 primary_rules,
194 );
195 }
196
197 if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
198 Self::replace_single_rule_node(
199 context.shared,
200 CascadeLevel::Animations,
201 LayerOrder::root(),
202 self.animation_rule(&context.shared)
203 .as_ref()
204 .map(|a| a.borrow_arc()),
205 primary_rules,
206 );
207 }
208 }
209
210 false
211 }
212
213 fn after_change_style(
215 &self,
216 context: &mut StyleContext<Self>,
217 primary_style: &Arc<ComputedValues>,
218 ) -> Option<Arc<ComputedValues>> {
219 StyleResolverForElement::new(
221 *self,
222 context,
223 RuleInclusion::All,
224 PseudoElementResolution::IfApplicable,
225 )
226 .after_change_style(primary_style)
227 }
228
229 fn needs_animations_update(
230 &self,
231 context: &mut StyleContext<Self>,
232 old_style: Option<&ComputedValues>,
233 new_style: &ComputedValues,
234 pseudo_element: Option<PseudoElement>,
235 ) -> bool {
236 let new_ui_style = new_style.get_ui();
237 let new_style_specifies_animations = new_ui_style.specifies_animations();
238
239 let has_animations = self.has_css_animations(&context.shared, pseudo_element);
240 if !new_style_specifies_animations && !has_animations {
241 return false;
242 }
243
244 let old_style = match old_style {
245 Some(old) => old,
246 None => {
260 return new_style_specifies_animations || new_style.is_pseudo_style();
261 },
262 };
263
264 let old_ui_style = old_style.get_ui();
265
266 let keyframes_could_have_changed = context
267 .shared
268 .traversal_flags
269 .contains(TraversalFlags::ForCSSRuleChanges);
270
271 if keyframes_could_have_changed {
279 return true;
280 }
281
282 if !old_ui_style.animations_equals(new_ui_style) {
284 return true;
285 }
286
287 let old_display = old_style.clone_display();
288 let new_display = new_style.clone_display();
289
290 if old_display == Display::None && new_display != Display::None {
292 return new_style_specifies_animations;
293 }
294
295 if old_display != Display::None && new_display == Display::None {
297 return has_animations;
298 }
299
300 if new_style.writing_mode != old_style.writing_mode {
305 return has_animations;
306 }
307
308 false
309 }
310
311 fn might_need_transitions_update(
312 &self,
313 context: &StyleContext<Self>,
314 old_style: Option<&ComputedValues>,
315 new_style: &ComputedValues,
316 pseudo_element: Option<PseudoElement>,
317 ) -> bool {
318 let old_style = match old_style {
319 Some(v) => v,
320 None => return false,
321 };
322
323 if !self.has_css_transitions(context.shared, pseudo_element)
324 && !new_style.get_ui().specifies_transitions()
325 {
326 return false;
327 }
328
329 if old_style.clone_display().is_none() {
330 return false;
331 }
332
333 return true;
334 }
335
336 #[cfg(feature = "gecko")]
337 fn maybe_resolve_starting_style(
338 &self,
339 context: &mut StyleContext<Self>,
340 old_values: Option<&Arc<ComputedValues>>,
341 new_styles: &ResolvedElementStyles,
342 ) -> Option<Arc<ComputedValues>> {
343 if !new_styles.may_have_starting_style()
348 || !new_styles.primary_style().get_ui().specifies_transitions()
349 {
350 return None;
351 }
352
353 if old_values.is_some()
356 && !new_styles
357 .primary_style()
358 .is_display_property_changed_from_none(old_values.map(|s| &**s))
359 {
360 return None;
361 }
362
363 let mut resolver = StyleResolverForElement::new(
370 *self,
371 context,
372 RuleInclusion::All,
373 PseudoElementResolution::IfApplicable,
374 );
375
376 let starting_style = resolver.resolve_starting_style().style;
377 if starting_style.style().clone_display().is_none() {
378 return None;
379 }
380
381 Some(starting_style.0)
382 }
383
384 #[cfg(feature = "gecko")]
391 fn process_transitions(
392 &self,
393 context: &mut StyleContext<Self>,
394 old_values: Option<&Arc<ComputedValues>>,
395 new_styles: &mut ResolvedElementStyles,
396 ) -> Option<Arc<ComputedValues>> {
397 let starting_values = self.maybe_resolve_starting_style(context, old_values, new_styles);
398 let before_change_or_starting = if starting_values.is_some() {
399 starting_values.as_ref()
400 } else {
401 old_values
402 };
403 let new_values = new_styles.primary_style_mut();
404
405 if !self.might_need_transitions_update(
406 context,
407 before_change_or_starting.map(|s| &**s),
408 new_values,
409 None,
410 ) {
411 return None;
412 }
413
414 let after_change_style =
415 if self.has_css_transitions(context.shared, None) {
416 self.after_change_style(context, new_values)
417 } else {
418 None
419 };
420
421 if !self.needs_transitions_update(
425 before_change_or_starting.unwrap(),
426 after_change_style.as_ref().unwrap_or(&new_values),
427 ) {
428 return None;
429 }
430
431 if let Some(values_without_transitions) = after_change_style {
432 *new_values = values_without_transitions;
433 }
434
435 if starting_values.is_some() {
437 starting_values
438 } else {
439 old_values.cloned()
440 }
441 }
442
443 #[cfg(feature = "gecko")]
444 fn process_animations(
445 &self,
446 context: &mut StyleContext<Self>,
447 old_styles: &mut ElementStyles,
448 new_styles: &mut ResolvedElementStyles,
449 important_rules_changed: bool,
450 ) {
451 use crate::context::UpdateAnimationsTasks;
452
453 let old_values = &old_styles.primary;
454 if context.shared.traversal_flags.for_animation_only() && old_values.is_some() {
455 return;
456 }
457
458 let mut tasks = UpdateAnimationsTasks::empty();
462
463 if old_values.as_deref().map_or_else(
464 || {
465 new_styles
466 .primary_style()
467 .get_ui()
468 .specifies_scroll_timelines()
469 },
470 |old| {
471 !old.get_ui()
472 .scroll_timelines_equals(new_styles.primary_style().get_ui())
473 },
474 ) {
475 tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES);
476 }
477
478 if old_values.as_deref().map_or_else(
479 || {
480 new_styles
481 .primary_style()
482 .get_ui()
483 .specifies_view_timelines()
484 },
485 |old| {
486 !old.get_ui()
487 .view_timelines_equals(new_styles.primary_style().get_ui())
488 },
489 ) {
490 tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES);
491 }
492
493 if self.needs_animations_update(
494 context,
495 old_values.as_deref(),
496 new_styles.primary_style(),
497 None,
498 ) {
499 tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
500 }
501
502 let before_change_style =
503 self.process_transitions(context, old_values.as_ref(), new_styles);
504 if before_change_style.is_some() {
505 tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS);
506 }
507
508 if self.has_animations(&context.shared) {
509 tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES);
510 if important_rules_changed {
511 tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);
512 }
513 if new_styles
514 .primary_style()
515 .is_display_property_changed_from_none(old_values.as_deref())
516 {
517 tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);
518 }
519 }
520
521 if !tasks.is_empty() {
522 let task = crate::context::SequentialTask::update_animations(
523 *self,
524 before_change_style,
525 tasks,
526 );
527 context.thread_local.tasks.push(task);
528 }
529 }
530
531 #[cfg(feature = "servo")]
532 fn process_animations(
533 &self,
534 context: &mut StyleContext<Self>,
535 old_styles: &mut ElementStyles,
536 new_resolved_styles: &mut ResolvedElementStyles,
537 _important_rules_changed: bool,
538 ) {
539 use crate::animation::AnimationSetKey;
540 use crate::dom::TDocument;
541
542 let style_changed = self.process_animations_for_style(
543 context,
544 &mut old_styles.primary,
545 new_resolved_styles.primary_style_mut(),
546 None,
547 );
548
549 if style_changed {
551 let primary_style = new_resolved_styles.primary_style();
552 let mut rule_node = primary_style.rules().clone();
553 let declarations = context.shared.animations.get_all_declarations(
554 &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()),
555 context.shared.current_time_for_animations,
556 self.as_node().owner_doc().shared_lock(),
557 );
558 Self::replace_single_rule_node(
559 &context.shared,
560 CascadeLevel::Transitions,
561 LayerOrder::root(),
562 declarations.transitions.as_ref().map(|a| a.borrow_arc()),
563 &mut rule_node,
564 );
565 Self::replace_single_rule_node(
566 &context.shared,
567 CascadeLevel::Animations,
568 LayerOrder::root(),
569 declarations.animations.as_ref().map(|a| a.borrow_arc()),
570 &mut rule_node,
571 );
572
573 if rule_node != *primary_style.rules() {
574 let inputs = CascadeInputs {
575 rules: Some(rule_node),
576 visited_rules: primary_style.visited_rules().cloned(),
577 flags: primary_style.flags.for_cascade_inputs(),
578 };
579
580 new_resolved_styles.primary.style = StyleResolverForElement::new(
581 *self,
582 context,
583 RuleInclusion::All,
584 PseudoElementResolution::IfApplicable,
585 )
586 .cascade_style_and_visited_with_default_parents(inputs);
587 }
588 }
589
590 self.process_animations_for_pseudo(
591 context,
592 old_styles,
593 new_resolved_styles,
594 PseudoElement::Before,
595 );
596 self.process_animations_for_pseudo(
597 context,
598 old_styles,
599 new_resolved_styles,
600 PseudoElement::After,
601 );
602 }
603
604 #[cfg(feature = "servo")]
605 fn process_animations_for_pseudo(
606 &self,
607 context: &mut StyleContext<Self>,
608 old_styles: &ElementStyles,
609 new_resolved_styles: &mut ResolvedElementStyles,
610 pseudo_element: PseudoElement,
611 ) {
612 use crate::animation::AnimationSetKey;
613 use crate::dom::TDocument;
614
615 let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone());
616 let style = match new_resolved_styles.pseudos.get(&pseudo_element) {
617 Some(style) => Arc::clone(style),
618 None => {
619 context
620 .shared
621 .animations
622 .cancel_all_animations_for_key(&key);
623 return;
624 },
625 };
626
627 let old_style = old_styles.pseudos.get(&pseudo_element).cloned();
628 self.process_animations_for_style(
629 context,
630 &old_style,
631 &style,
632 Some(pseudo_element.clone()),
633 );
634
635 let declarations = context.shared.animations.get_all_declarations(
636 &key,
637 context.shared.current_time_for_animations,
638 self.as_node().owner_doc().shared_lock(),
639 );
640 if declarations.is_empty() {
641 return;
642 }
643
644 let mut rule_node = style.rules().clone();
645 Self::replace_single_rule_node(
646 &context.shared,
647 CascadeLevel::Transitions,
648 LayerOrder::root(),
649 declarations.transitions.as_ref().map(|a| a.borrow_arc()),
650 &mut rule_node,
651 );
652 Self::replace_single_rule_node(
653 &context.shared,
654 CascadeLevel::Animations,
655 LayerOrder::root(),
656 declarations.animations.as_ref().map(|a| a.borrow_arc()),
657 &mut rule_node,
658 );
659 if rule_node == *style.rules() {
660 return;
661 }
662
663 let inputs = CascadeInputs {
664 rules: Some(rule_node),
665 visited_rules: style.visited_rules().cloned(),
666 flags: style.flags.for_cascade_inputs(),
667 };
668
669 let new_style = StyleResolverForElement::new(
670 *self,
671 context,
672 RuleInclusion::All,
673 PseudoElementResolution::IfApplicable,
674 )
675 .cascade_style_and_visited_for_pseudo_with_default_parents(
676 inputs,
677 &pseudo_element,
678 &new_resolved_styles.primary,
679 );
680
681 new_resolved_styles
682 .pseudos
683 .set(&pseudo_element, new_style.0);
684 }
685
686 #[cfg(feature = "servo")]
687 fn process_animations_for_style(
688 &self,
689 context: &mut StyleContext<Self>,
690 old_values: &Option<Arc<ComputedValues>>,
691 new_values: &Arc<ComputedValues>,
692 pseudo_element: Option<PseudoElement>,
693 ) -> bool {
694 use crate::animation::{AnimationSetKey, AnimationState};
695
696 let needs_animations_update = self.needs_animations_update(
699 context,
700 old_values.as_deref(),
701 new_values,
702 pseudo_element,
703 );
704
705 let might_need_transitions_update = self.might_need_transitions_update(
706 context,
707 old_values.as_deref(),
708 new_values,
709 pseudo_element,
710 );
711
712 let mut after_change_style = None;
713 if might_need_transitions_update {
714 after_change_style = self.after_change_style(context, new_values);
715 }
716
717 let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
718 let shared_context = context.shared;
719 let mut animation_set = shared_context
720 .animations
721 .sets
722 .write()
723 .remove(&key)
724 .unwrap_or_default();
725
726 if needs_animations_update {
730 let mut resolver = StyleResolverForElement::new(
731 *self,
732 context,
733 RuleInclusion::All,
734 PseudoElementResolution::IfApplicable,
735 );
736
737 animation_set.update_animations_for_new_style::<Self>(
738 *self,
739 &shared_context,
740 &new_values,
741 &mut resolver,
742 );
743 }
744
745 animation_set.update_transitions_for_new_style(
746 might_need_transitions_update,
747 &shared_context,
748 old_values.as_ref(),
749 after_change_style.as_ref().unwrap_or(new_values),
750 );
751
752 animation_set
755 .transitions
756 .retain(|transition| transition.state != AnimationState::Finished);
757
758 animation_set
759 .animations
760 .retain(|animation| animation.state != AnimationState::Finished);
761
762 let changed_animations = animation_set.dirty;
765 if !animation_set.is_empty() {
766 animation_set.dirty = false;
767 shared_context
768 .animations
769 .sets
770 .write()
771 .insert(key, animation_set);
772 }
773
774 changed_animations
775 }
776
777 fn accumulate_damage_for(
779 &self,
780 shared_context: &SharedStyleContext,
781 damage: &mut RestyleDamage,
782 old_values: &ComputedValues,
783 new_values: &ComputedValues,
784 pseudo: Option<&PseudoElement>,
785 ) -> ChildRestyleRequirement {
786 debug!("accumulate_damage_for: {:?}", self);
787 debug_assert!(!shared_context
788 .traversal_flags
789 .contains(TraversalFlags::FinalAnimationTraversal));
790
791 let difference = self.compute_style_difference(old_values, new_values, pseudo);
792
793 *damage |= difference.damage;
794
795 debug!(" > style difference: {:?}", difference);
796
797 if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() {
800 debug!(
801 " > flags changed: {:?} != {:?}",
802 old_values.flags, new_values.flags
803 );
804 return ChildRestyleRequirement::MustCascadeChildren;
805 }
806
807 if old_values.effective_zoom != new_values.effective_zoom {
808 debug!(
810 " > zoom changed: {:?} != {:?}",
811 old_values.effective_zoom, new_values.effective_zoom
812 );
813 return ChildRestyleRequirement::MustCascadeChildren;
814 }
815
816 match difference.change {
817 StyleChange::Unchanged => return ChildRestyleRequirement::CanSkipCascade,
818 StyleChange::Changed { reset_only } => {
819 if !reset_only {
822 return ChildRestyleRequirement::MustCascadeChildren;
823 }
824 },
825 }
826
827 let old_display = old_values.clone_display();
828 let new_display = new_values.clone_display();
829
830 if old_display != new_display {
831 if old_display == Display::None {
834 return ChildRestyleRequirement::MustCascadeChildren;
835 }
836 if old_display.is_item_container() != new_display.is_item_container() {
840 return ChildRestyleRequirement::MustCascadeChildren;
841 }
842 if old_display.is_contents() || new_display.is_contents() {
846 return ChildRestyleRequirement::MustCascadeChildren;
847 }
848 #[cfg(feature = "gecko")]
851 {
852 if old_display.is_ruby_type() != new_display.is_ruby_type() {
853 return ChildRestyleRequirement::MustCascadeChildren;
854 }
855 }
856 }
857
858 #[cfg(feature = "gecko")]
864 {
865 use crate::values::specified::align::AlignFlags;
866
867 let old_justify_items = old_values.get_position().clone_justify_items();
868 let new_justify_items = new_values.get_position().clone_justify_items();
869
870 let was_legacy_justify_items =
871 old_justify_items.computed.0.contains(AlignFlags::LEGACY);
872
873 let is_legacy_justify_items = new_justify_items.computed.0.contains(AlignFlags::LEGACY);
874
875 if is_legacy_justify_items != was_legacy_justify_items {
876 return ChildRestyleRequirement::MustCascadeChildren;
877 }
878
879 if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed
880 {
881 return ChildRestyleRequirement::MustCascadeChildren;
882 }
883 }
884
885 #[cfg(feature = "servo")]
886 {
887 if old_values.is_multicol() != new_values.is_multicol() {
890 return ChildRestyleRequirement::MustCascadeChildren;
891 }
892 }
893
894 ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle
897 }
898}
899
900impl<E: TElement> PrivateMatchMethods for E {}
901
902pub trait MatchMethods: TElement {
904 fn layout_parent(&self) -> Self {
915 let mut current = self.clone();
916 loop {
917 current = match current.traversal_parent() {
918 Some(el) => el,
919 None => return current,
920 };
921
922 let is_display_contents = current
923 .borrow_data()
924 .unwrap()
925 .styles
926 .primary()
927 .is_display_contents();
928
929 if !is_display_contents {
930 return current;
931 }
932 }
933 }
934
935 fn finish_restyle(
938 &self,
939 context: &mut StyleContext<Self>,
940 data: &mut ElementData,
941 mut new_styles: ResolvedElementStyles,
942 important_rules_changed: bool,
943 ) -> ChildRestyleRequirement {
944 use std::cmp;
945
946 self.process_animations(
947 context,
948 &mut data.styles,
949 &mut new_styles,
950 important_rules_changed,
951 );
952
953 let old_styles = data.set_styles(new_styles);
955
956 let new_primary_style = data.styles.primary.as_ref().unwrap();
957
958 let mut restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
959 let is_root = new_primary_style
960 .flags
961 .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
962 let is_container = !new_primary_style
963 .get_box()
964 .clone_container_type()
965 .is_normal();
966 if is_root || is_container {
967 let device = context.shared.stylist.device();
968 let old_style = old_styles.primary.as_ref();
969 let new_font_size = new_primary_style.get_font().clone_font_size();
970 let old_font_size = old_style.map(|s| s.get_font().clone_font_size());
971
972 if old_font_size != Some(new_font_size) {
973 if is_root {
974 debug_assert!(self.owner_doc_matches_for_testing(device));
975 let size = new_font_size.computed_size();
976 device.set_root_font_size(new_primary_style.effective_zoom.unzoom(size.px()));
977 if device.used_root_font_size() {
978 restyle_requirement = ChildRestyleRequirement::MustCascadeDescendants;
982 }
983 }
984
985 if is_container && old_font_size.is_some() {
986 restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
993 }
994 }
995
996 let new_line_height = device
999 .calc_line_height(
1000 &new_primary_style.get_font(),
1001 new_primary_style.writing_mode,
1002 None,
1003 )
1004 .0;
1005 let old_line_height = old_style.map(|s| {
1006 device
1007 .calc_line_height(&s.get_font(), s.writing_mode, None)
1008 .0
1009 });
1010
1011 if old_line_height != Some(new_line_height) {
1012 if is_root {
1013 debug_assert!(self.owner_doc_matches_for_testing(device));
1014 device.set_root_line_height(
1015 new_primary_style
1016 .effective_zoom
1017 .unzoom(new_line_height.px()),
1018 );
1019 if device.used_root_line_height() {
1020 restyle_requirement = std::cmp::max(
1021 restyle_requirement,
1022 ChildRestyleRequirement::MustCascadeDescendants,
1023 );
1024 }
1025 }
1026
1027 if is_container && old_line_height.is_some() {
1028 restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
1029 }
1030 }
1031 }
1032
1033 if context.shared.stylist.quirks_mode() == QuirksMode::Quirks {
1034 if self.is_html_document_body_element() {
1035 let device = context.shared.stylist.device();
1040
1041 let text_color = new_primary_style.get_inherited_text().clone_color();
1043 device.set_body_text_color(text_color);
1044 }
1045 }
1046
1047 if context
1049 .shared
1050 .traversal_flags
1051 .contains(TraversalFlags::FinalAnimationTraversal)
1052 {
1053 return ChildRestyleRequirement::MustCascadeChildren;
1054 }
1055
1056 let old_primary_style = match old_styles.primary {
1058 Some(s) => s,
1059 None => return ChildRestyleRequirement::MustCascadeChildren,
1060 };
1061
1062 let old_container_type = old_primary_style.clone_container_type();
1063 let new_container_type = new_primary_style.clone_container_type();
1064 if old_container_type != new_container_type && !new_container_type.is_size_container_type()
1065 {
1066 restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
1069 } else if old_container_type.is_size_container_type()
1070 && !old_primary_style.is_display_contents()
1071 && new_primary_style.is_display_contents()
1072 {
1073 restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
1076 }
1077
1078 restyle_requirement = cmp::max(
1079 restyle_requirement,
1080 self.accumulate_damage_for(
1081 context.shared,
1082 &mut data.damage,
1083 &old_primary_style,
1084 new_primary_style,
1085 None,
1086 ),
1087 );
1088
1089 if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
1090 return restyle_requirement;
1092 }
1093
1094 let pseudo_styles = old_styles
1095 .pseudos
1096 .as_array()
1097 .iter()
1098 .zip(data.styles.pseudos.as_array().iter());
1099
1100 for (i, (old, new)) in pseudo_styles.enumerate() {
1101 match (old, new) {
1102 (&Some(ref old), &Some(ref new)) => {
1103 self.accumulate_damage_for(
1104 context.shared,
1105 &mut data.damage,
1106 old,
1107 new,
1108 Some(&PseudoElement::from_eager_index(i)),
1109 );
1110 },
1111 (&None, &None) => {},
1112 _ => {
1113 let pseudo = PseudoElement::from_eager_index(i);
1118 let new_pseudo_should_exist =
1119 new.as_ref().map_or(false, |s| pseudo.should_exist(s));
1120 let old_pseudo_should_exist =
1121 old.as_ref().map_or(false, |s| pseudo.should_exist(s));
1122 if new_pseudo_should_exist != old_pseudo_should_exist {
1123 data.damage |= RestyleDamage::reconstruct();
1124 return restyle_requirement;
1125 }
1126 },
1127 }
1128 }
1129
1130 restyle_requirement
1131 }
1132
1133 fn replace_rules(
1138 &self,
1139 replacements: RestyleHint,
1140 context: &mut StyleContext<Self>,
1141 cascade_inputs: &mut ElementCascadeInputs,
1142 ) -> bool {
1143 let mut result = false;
1144 result |= self.replace_rules_internal(
1145 replacements,
1146 context,
1147 CascadeVisitedMode::Unvisited,
1148 cascade_inputs,
1149 );
1150 result |= self.replace_rules_internal(
1151 replacements,
1152 context,
1153 CascadeVisitedMode::Visited,
1154 cascade_inputs,
1155 );
1156 result
1157 }
1158
1159 fn compute_style_difference(
1163 &self,
1164 old_values: &ComputedValues,
1165 new_values: &ComputedValues,
1166 pseudo: Option<&PseudoElement>,
1167 ) -> StyleDifference {
1168 debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
1169 RestyleDamage::compute_style_difference::<Self>(old_values, new_values)
1170 }
1171}
1172
1173impl<E: TElement> MatchMethods for E {}