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, CascadeOrigin, StrongRuleNode};
24#[cfg(feature = "servo")]
25use crate::rule_tree::RuleCascadeFlags;
26use crate::selector_parser::{PseudoElement, RestyleDamage};
27use crate::shared_lock::Locked;
28use crate::style_resolver::StyleResolverForElement;
29use crate::style_resolver::{PseudoElementResolution, ResolvedElementStyles};
30use crate::stylesheets::layer_rule::LayerOrder;
31use crate::stylist::RuleInclusion;
32use crate::traversal_flags::TraversalFlags;
33use servo_arc::{Arc, ArcBorrow};
34
35#[derive(Debug)]
37pub struct StyleDifference {
38 pub damage: RestyleDamage,
40
41 pub change: StyleChange,
43}
44
45#[derive(Clone, Copy, Debug)]
47pub enum StyleChange {
48 Unchanged,
50 Changed {
52 reset_only: bool,
54 custom_properties_changed: bool,
56 },
57}
58
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61enum CascadeVisitedMode {
62 Unvisited,
64 Visited,
68}
69
70trait PrivateMatchMethods: TElement {
71 fn replace_single_rule_node(
72 context: &SharedStyleContext,
73 level: CascadeLevel,
74 layer_order: LayerOrder,
75 pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
76 path: &mut StrongRuleNode,
77 ) -> bool {
78 let stylist = &context.stylist;
79 let guards = &context.guards;
80
81 let mut important_rules_changed = false;
82 let new_node = stylist.rule_tree().update_rule_at_level(
83 level,
84 layer_order,
85 pdb,
86 path,
87 guards,
88 &mut important_rules_changed,
89 );
90 if let Some(n) = new_node {
91 *path = n;
92 }
93 important_rules_changed
94 }
95
96 fn replace_rules_internal(
101 &self,
102 replacements: RestyleHint,
103 context: &mut StyleContext<Self>,
104 cascade_visited: CascadeVisitedMode,
105 cascade_inputs: &mut ElementCascadeInputs,
106 ) -> bool {
107 debug_assert!(
108 replacements.intersects(RestyleHint::replacements())
109 && (replacements & !RestyleHint::replacements()).is_empty()
110 );
111
112 let primary_rules = match cascade_visited {
113 CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(),
114 CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(),
115 };
116
117 let primary_rules = match primary_rules {
118 Some(r) => r,
119 None => return false,
120 };
121
122 if !context.shared.traversal_flags.for_animation_only() {
123 let mut result = false;
124 if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
125 let style_attribute = self.style_attribute();
126 result |= Self::replace_single_rule_node(
127 context.shared,
128 CascadeLevel::same_tree_author_normal(),
129 LayerOrder::style_attribute(),
130 style_attribute,
131 primary_rules,
132 );
133 result |= Self::replace_single_rule_node(
134 context.shared,
135 CascadeLevel::same_tree_author_important(),
136 LayerOrder::style_attribute(),
137 style_attribute,
138 primary_rules,
139 );
140 self.unset_dirty_style_attribute();
142 }
143 return result;
144 }
145
146 if replacements.intersects(RestyleHint::for_animations()) {
152 debug_assert!(context.shared.traversal_flags.for_animation_only());
153
154 if replacements.contains(RestyleHint::RESTYLE_SMIL) {
155 Self::replace_single_rule_node(
156 context.shared,
157 CascadeLevel::new(CascadeOrigin::SMILOverride),
158 LayerOrder::root(),
159 self.smil_override(),
160 primary_rules,
161 );
162 }
163
164 if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
165 Self::replace_single_rule_node(
166 context.shared,
167 CascadeLevel::new(CascadeOrigin::Transitions),
168 LayerOrder::root(),
169 self.transition_rule(&context.shared)
170 .as_ref()
171 .map(|a| a.borrow_arc()),
172 primary_rules,
173 );
174 }
175
176 if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
177 Self::replace_single_rule_node(
178 context.shared,
179 CascadeLevel::new(CascadeOrigin::Animations),
180 LayerOrder::root(),
181 self.animation_rule(&context.shared)
182 .as_ref()
183 .map(|a| a.borrow_arc()),
184 primary_rules,
185 );
186 }
187 }
188
189 false
190 }
191
192 fn after_change_style(
194 &self,
195 context: &mut StyleContext<Self>,
196 primary_style: &Arc<ComputedValues>,
197 ) -> Option<Arc<ComputedValues>> {
198 StyleResolverForElement::new(
200 *self,
201 context,
202 RuleInclusion::All,
203 PseudoElementResolution::IfApplicable,
204 )
205 .after_change_style(primary_style)
206 }
207
208 fn needs_animations_update(
209 &self,
210 context: &mut StyleContext<Self>,
211 old_style: Option<&ComputedValues>,
212 new_style: &ComputedValues,
213 pseudo_element: Option<PseudoElement>,
214 ) -> bool {
215 let new_ui_style = new_style.get_ui();
216 let new_style_specifies_animations = new_ui_style.specifies_animations();
217
218 let has_animations = self.has_css_animations(&context.shared, pseudo_element);
219 if !new_style_specifies_animations && !has_animations {
220 return false;
221 }
222
223 let old_style = match old_style {
224 Some(old) => old,
225 None => {
239 return new_style_specifies_animations || new_style.is_pseudo_style();
240 },
241 };
242
243 let old_ui_style = old_style.get_ui();
244
245 let keyframes_could_have_changed = context
246 .shared
247 .traversal_flags
248 .contains(TraversalFlags::ForCSSRuleChanges);
249
250 if keyframes_could_have_changed {
258 return true;
259 }
260
261 if !old_ui_style.animations_equals(new_ui_style) {
263 return true;
264 }
265
266 let old_display = old_style.clone_display();
267 let new_display = new_style.clone_display();
268
269 if old_display == Display::None && new_display != Display::None {
271 return new_style_specifies_animations;
272 }
273
274 if old_display != Display::None && new_display == Display::None {
276 return has_animations;
277 }
278
279 if new_style.writing_mode != old_style.writing_mode {
284 return has_animations;
285 }
286
287 false
288 }
289
290 fn might_need_transitions_update(
291 &self,
292 context: &StyleContext<Self>,
293 old_style: Option<&ComputedValues>,
294 new_style: &ComputedValues,
295 pseudo_element: Option<PseudoElement>,
296 ) -> bool {
297 let old_style = match old_style {
298 Some(v) => v,
299 None => return false,
300 };
301
302 if !self.has_css_transitions(context.shared, pseudo_element)
303 && !new_style.get_ui().specifies_transitions()
304 {
305 return false;
306 }
307
308 if old_style.clone_display().is_none() {
309 return false;
310 }
311
312 return true;
313 }
314
315 #[cfg(feature = "gecko")]
316 fn maybe_resolve_starting_style(
317 &self,
318 context: &mut StyleContext<Self>,
319 old_values: Option<&Arc<ComputedValues>>,
320 new_styles: &ResolvedElementStyles,
321 ) -> Option<Arc<ComputedValues>> {
322 let new_primary = new_styles.primary_style();
325 if !new_primary.get_ui().specifies_transitions() {
326 return None;
327 }
328
329 if old_values.is_some()
332 && !new_primary.is_display_property_changed_from_none(old_values.map(|s| &**s))
333 {
334 return None;
335 }
336
337 let mut resolver = StyleResolverForElement::new(
338 *self,
339 context,
340 RuleInclusion::All,
341 PseudoElementResolution::IfApplicable,
342 );
343
344 let starting_style = resolver.resolve_starting_style(new_primary)?;
345 if starting_style.style().clone_display().is_none() {
346 return None;
347 }
348
349 Some(starting_style.0)
350 }
351
352 #[cfg(feature = "gecko")]
359 fn process_transitions(
360 &self,
361 context: &mut StyleContext<Self>,
362 old_values: Option<&Arc<ComputedValues>>,
363 new_styles: &mut ResolvedElementStyles,
364 ) -> Option<Arc<ComputedValues>> {
365 let starting_values = self.maybe_resolve_starting_style(context, old_values, new_styles);
366 let before_change_or_starting = starting_values.as_ref().or(old_values);
367 let new_values = new_styles.primary_style_mut();
368
369 if !self.might_need_transitions_update(
370 context,
371 before_change_or_starting.map(|s| &**s),
372 new_values,
373 None,
374 ) {
375 return None;
376 }
377
378 let after_change_style =
379 if self.has_css_transitions(context.shared, None) {
380 self.after_change_style(context, new_values)
381 } else {
382 None
383 };
384
385 if !self.needs_transitions_update(
389 before_change_or_starting.unwrap(),
390 after_change_style.as_ref().unwrap_or(&new_values),
391 ) {
392 return None;
393 }
394
395 if let Some(values_without_transitions) = after_change_style {
396 *new_values = values_without_transitions;
397 }
398
399 if starting_values.is_some() {
401 starting_values
402 } else {
403 old_values.cloned()
404 }
405 }
406
407 #[cfg(feature = "gecko")]
408 fn process_animations(
409 &self,
410 context: &mut StyleContext<Self>,
411 old_styles: &mut ElementStyles,
412 new_styles: &mut ResolvedElementStyles,
413 important_rules_changed: bool,
414 ) {
415 use crate::context::UpdateAnimationsTasks;
416
417 let old_values = &old_styles.primary;
418 if context.shared.traversal_flags.for_animation_only() && old_values.is_some() {
419 return;
420 }
421
422 let mut tasks = UpdateAnimationsTasks::empty();
426
427 if old_values.as_deref().map_or_else(
428 || {
429 new_styles
430 .primary_style()
431 .get_ui()
432 .specifies_timeline_scope()
433 },
434 |old| {
435 !old.get_ui()
436 .timeline_scope_equals(new_styles.primary_style().get_ui())
437 },
438 ) {
439 tasks.insert(UpdateAnimationsTasks::TIMELINE_SCOPES);
440 }
441
442 if old_values.as_deref().map_or_else(
443 || {
444 new_styles
445 .primary_style()
446 .get_ui()
447 .specifies_scroll_timelines()
448 },
449 |old| {
450 !old.get_ui()
451 .scroll_timelines_equals(new_styles.primary_style().get_ui())
452 },
453 ) {
454 tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES);
455 }
456
457 if old_values.as_deref().map_or_else(
458 || {
459 new_styles
460 .primary_style()
461 .get_ui()
462 .specifies_view_timelines()
463 },
464 |old| {
465 !old.get_ui()
466 .view_timelines_equals(new_styles.primary_style().get_ui())
467 },
468 ) {
469 tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES);
470 }
471
472 if self.needs_animations_update(
473 context,
474 old_values.as_deref(),
475 new_styles.primary_style(),
476 None,
477 ) {
478 tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
479 }
480
481 let before_change_style =
482 self.process_transitions(context, old_values.as_ref(), new_styles);
483 if before_change_style.is_some() {
484 tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS);
485 }
486
487 if self.has_animations(&context.shared) {
488 tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES);
489 if important_rules_changed {
490 tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);
491 }
492 if new_styles
493 .primary_style()
494 .is_display_property_changed_from_none(old_values.as_deref())
495 {
496 tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);
497 }
498 }
499
500 if !tasks.is_empty() {
501 let task = crate::context::SequentialTask::update_animations(
502 *self,
503 before_change_style,
504 tasks,
505 );
506 context.thread_local.tasks.push(task);
507 }
508 }
509
510 #[cfg(feature = "servo")]
511 fn process_animations(
512 &self,
513 context: &mut StyleContext<Self>,
514 old_styles: &mut ElementStyles,
515 new_resolved_styles: &mut ResolvedElementStyles,
516 _important_rules_changed: bool,
517 ) {
518 use crate::animation::AnimationSetKey;
519 use crate::dom::TDocument;
520
521 let style_changed = self.process_animations_for_style(
522 context,
523 &mut old_styles.primary,
524 new_resolved_styles.primary_style_mut(),
525 None,
526 );
527
528 if style_changed {
530 let primary_style = new_resolved_styles.primary_style();
531 let mut rule_node = primary_style.rules().clone();
532 let declarations = context.shared.animations.get_all_declarations(
533 &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()),
534 context.shared.current_time_for_animations,
535 self.as_node().owner_doc().shared_lock(),
536 );
537 Self::replace_single_rule_node(
538 &context.shared,
539 CascadeLevel::new(CascadeOrigin::Transitions),
540 LayerOrder::root(),
541 declarations.transitions.as_ref().map(|a| a.borrow_arc()),
542 &mut rule_node,
543 );
544 Self::replace_single_rule_node(
545 &context.shared,
546 CascadeLevel::new(CascadeOrigin::Animations),
547 LayerOrder::root(),
548 declarations.animations.as_ref().map(|a| a.borrow_arc()),
549 &mut rule_node,
550 );
551
552 if rule_node != *primary_style.rules() {
553 let inputs = CascadeInputs {
554 rules: Some(rule_node),
555 visited_rules: primary_style.visited_rules().cloned(),
556 flags: primary_style.flags.for_cascade_inputs(),
557 included_cascade_flags: RuleCascadeFlags::empty(),
558 };
559
560 new_resolved_styles.primary.style = StyleResolverForElement::new(
561 *self,
562 context,
563 RuleInclusion::All,
564 PseudoElementResolution::IfApplicable,
565 )
566 .cascade_style_and_visited_with_default_parents(inputs);
567 }
568 }
569
570 self.process_animations_for_pseudo(
571 context,
572 old_styles,
573 new_resolved_styles,
574 PseudoElement::Before,
575 );
576 self.process_animations_for_pseudo(
577 context,
578 old_styles,
579 new_resolved_styles,
580 PseudoElement::After,
581 );
582 }
583
584 #[cfg(feature = "servo")]
585 fn process_animations_for_pseudo(
586 &self,
587 context: &mut StyleContext<Self>,
588 old_styles: &ElementStyles,
589 new_resolved_styles: &mut ResolvedElementStyles,
590 pseudo_element: PseudoElement,
591 ) {
592 use crate::animation::AnimationSetKey;
593 use crate::dom::TDocument;
594
595 let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone());
596 let style = match new_resolved_styles.pseudos.get(&pseudo_element) {
597 Some(style) => Arc::clone(style),
598 None => {
599 context
600 .shared
601 .animations
602 .cancel_all_animations_for_key(&key);
603 return;
604 },
605 };
606
607 let old_style = old_styles.pseudos.get(&pseudo_element).cloned();
608 self.process_animations_for_style(
609 context,
610 &old_style,
611 &style,
612 Some(pseudo_element.clone()),
613 );
614
615 let declarations = context.shared.animations.get_all_declarations(
616 &key,
617 context.shared.current_time_for_animations,
618 self.as_node().owner_doc().shared_lock(),
619 );
620 if declarations.is_empty() {
621 return;
622 }
623
624 let mut rule_node = style.rules().clone();
625 Self::replace_single_rule_node(
626 &context.shared,
627 CascadeLevel::new(CascadeOrigin::Transitions),
628 LayerOrder::root(),
629 declarations.transitions.as_ref().map(|a| a.borrow_arc()),
630 &mut rule_node,
631 );
632 Self::replace_single_rule_node(
633 &context.shared,
634 CascadeLevel::new(CascadeOrigin::Animations),
635 LayerOrder::root(),
636 declarations.animations.as_ref().map(|a| a.borrow_arc()),
637 &mut rule_node,
638 );
639 if rule_node == *style.rules() {
640 return;
641 }
642
643 let inputs = CascadeInputs {
644 rules: Some(rule_node),
645 visited_rules: style.visited_rules().cloned(),
646 flags: style.flags.for_cascade_inputs(),
647 included_cascade_flags: RuleCascadeFlags::empty(),
648 };
649
650 let new_style = StyleResolverForElement::new(
651 *self,
652 context,
653 RuleInclusion::All,
654 PseudoElementResolution::IfApplicable,
655 )
656 .cascade_style_and_visited_for_pseudo_with_default_parents(
657 inputs,
658 &pseudo_element,
659 &new_resolved_styles.primary,
660 );
661
662 new_resolved_styles
663 .pseudos
664 .set(&pseudo_element, new_style.0);
665 }
666
667 #[cfg(feature = "servo")]
668 fn process_animations_for_style(
669 &self,
670 context: &mut StyleContext<Self>,
671 old_values: &Option<Arc<ComputedValues>>,
672 new_values: &Arc<ComputedValues>,
673 pseudo_element: Option<PseudoElement>,
674 ) -> bool {
675 use crate::animation::{AnimationSetKey, AnimationState};
676
677 let needs_animations_update = self.needs_animations_update(
680 context,
681 old_values.as_deref(),
682 new_values,
683 pseudo_element,
684 );
685
686 let might_need_transitions_update = self.might_need_transitions_update(
687 context,
688 old_values.as_deref(),
689 new_values,
690 pseudo_element,
691 );
692
693 let mut after_change_style = None;
694 if might_need_transitions_update {
695 after_change_style = self.after_change_style(context, new_values);
696 }
697
698 let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
699 let shared_context = context.shared;
700 let mut animation_set = shared_context
701 .animations
702 .sets
703 .write()
704 .remove(&key)
705 .unwrap_or_default();
706
707 if needs_animations_update {
711 let mut resolver = StyleResolverForElement::new(
712 *self,
713 context,
714 RuleInclusion::All,
715 PseudoElementResolution::IfApplicable,
716 );
717
718 animation_set.update_animations_for_new_style::<Self>(
719 *self,
720 &shared_context,
721 &new_values,
722 &mut resolver,
723 );
724 }
725
726 animation_set.update_transitions_for_new_style(
727 might_need_transitions_update,
728 &shared_context,
729 old_values.as_ref(),
730 after_change_style.as_ref().unwrap_or(new_values),
731 );
732
733 animation_set
736 .transitions
737 .retain(|transition| transition.state != AnimationState::Finished);
738
739 animation_set
740 .animations
741 .retain(|animation| animation.state != AnimationState::Finished);
742
743 let changed_animations = animation_set.dirty;
746 if !animation_set.is_empty() {
747 animation_set.dirty = false;
748 shared_context
749 .animations
750 .sets
751 .write()
752 .insert(key, animation_set);
753 }
754
755 changed_animations
756 }
757
758 fn accumulate_damage_for(
760 &self,
761 shared_context: &SharedStyleContext,
762 damage: &mut RestyleDamage,
763 old_values: &ComputedValues,
764 new_values: &ComputedValues,
765 pseudo: Option<&PseudoElement>,
766 ) -> RestyleHint {
767 debug!("accumulate_damage_for: {:?}", self);
768 debug_assert!(!shared_context
769 .traversal_flags
770 .contains(TraversalFlags::FinalAnimationTraversal));
771
772 let difference = self.compute_style_difference(old_values, new_values, pseudo);
773
774 *damage |= difference.damage;
775
776 debug!(" > style difference: {:?}", difference);
777
778 if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() {
781 debug!(
782 " > flags changed: {:?} != {:?}",
783 old_values.flags, new_values.flags
784 );
785 return RestyleHint::RECASCADE_SELF;
786 }
787
788 if old_values.effective_zoom != new_values.effective_zoom {
789 debug!(
791 " > zoom changed: {:?} != {:?}",
792 old_values.effective_zoom, new_values.effective_zoom
793 );
794 return RestyleHint::RECASCADE_SELF;
795 }
796
797 match difference.change {
798 StyleChange::Unchanged => return RestyleHint::empty(),
799 StyleChange::Changed {
800 reset_only,
801 custom_properties_changed,
802 } => {
803 if custom_properties_changed {
804 return RestyleHint::RECASCADE_SELF
805 | RestyleHint::RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES;
806 }
807 if !reset_only {
810 return RestyleHint::RECASCADE_SELF;
811 }
812 },
813 }
814
815 let old_display = old_values.clone_display();
816 let new_display = new_values.clone_display();
817
818 if old_display != new_display {
819 if old_display == Display::None {
822 return RestyleHint::RECASCADE_SELF;
823 }
824 if old_display.is_item_container() != new_display.is_item_container() {
828 return RestyleHint::RECASCADE_SELF;
829 }
830 if old_display.is_contents() || new_display.is_contents() {
834 return RestyleHint::RECASCADE_SELF;
835 }
836 #[cfg(feature = "gecko")]
839 {
840 if old_display.is_ruby_type() != new_display.is_ruby_type() {
841 return RestyleHint::RECASCADE_SELF;
842 }
843 }
844 }
845
846 #[cfg(feature = "gecko")]
852 {
853 use crate::values::specified::align::AlignFlags;
854
855 let old_justify_items = old_values.get_position().clone_justify_items();
856 let new_justify_items = new_values.get_position().clone_justify_items();
857
858 let was_legacy_justify_items = old_justify_items.computed.contains(AlignFlags::LEGACY);
859
860 let is_legacy_justify_items = new_justify_items.computed.contains(AlignFlags::LEGACY);
861
862 if is_legacy_justify_items != was_legacy_justify_items {
863 return RestyleHint::RECASCADE_SELF;
864 }
865
866 if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed
867 {
868 return RestyleHint::RECASCADE_SELF;
869 }
870 }
871
872 #[cfg(feature = "servo")]
873 {
874 if old_values.is_multicol() != new_values.is_multicol() {
877 return RestyleHint::RECASCADE_SELF;
878 }
879 }
880
881 RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE
884 }
885}
886
887impl<E: TElement> PrivateMatchMethods for E {}
888
889pub trait MatchMethods: TElement {
891 fn layout_parent(&self) -> Self {
902 let mut current = self.clone();
903 loop {
904 current = match current.traversal_parent() {
905 Some(el) => el,
906 None => return current,
907 };
908
909 let is_display_contents = current
910 .borrow_data()
911 .unwrap()
912 .styles
913 .primary()
914 .is_display_contents();
915
916 if !is_display_contents {
917 return current;
918 }
919 }
920 }
921
922 fn finish_restyle(
925 &self,
926 context: &mut StyleContext<Self>,
927 data: &mut ElementData,
928 mut new_styles: ResolvedElementStyles,
929 important_rules_changed: bool,
930 ) -> RestyleHint {
931 self.process_animations(
932 context,
933 &mut data.styles,
934 &mut new_styles,
935 important_rules_changed,
936 );
937
938 let old_styles = data.set_styles(new_styles);
940
941 let new_primary_style = data.styles.primary.as_ref().unwrap();
942
943 let mut child_restyle_hint = RestyleHint::empty();
944 let is_root = new_primary_style
945 .flags
946 .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
947 let is_container = !new_primary_style
948 .get_box()
949 .clone_container_type()
950 .is_normal();
951 if is_root || is_container {
952 let device = context.shared.stylist.device();
953 let old_style = old_styles.primary.as_ref();
954 let new_font_size = new_primary_style.get_font().clone_font_size();
955 let old_font_size = old_style.map(|s| s.get_font().clone_font_size());
956
957 let new_line_height = device
960 .calc_line_height(
961 &new_primary_style.get_font(),
962 new_primary_style.writing_mode,
963 None,
964 )
965 .0;
966 let old_line_height = old_style.map(|s| {
967 device
968 .calc_line_height(&s.get_font(), s.writing_mode, None)
969 .0
970 });
971
972 if is_root {
975 debug_assert!(self.owner_doc_matches_for_testing(device));
976 device.set_root_style(new_primary_style);
977
978 if old_font_size != Some(new_font_size) {
980 let size = new_font_size.computed_size();
981 device.set_root_font_size(new_primary_style.effective_zoom.unzoom(size.px()));
982 if device.used_root_font_size() {
983 child_restyle_hint |= RestyleHint::recascade_subtree();
984 }
985 }
986
987 if old_line_height != Some(new_line_height) {
989 device.set_root_line_height(
990 new_primary_style
991 .effective_zoom
992 .unzoom(new_line_height.px()),
993 );
994 if device.used_root_line_height() {
995 child_restyle_hint |= RestyleHint::recascade_subtree();
996 }
997 }
998
999 if device.used_root_font_metrics() && device.update_root_font_metrics() {
1003 child_restyle_hint |= RestyleHint::recascade_subtree();
1004 }
1005 }
1006
1007 if is_container
1008 && (old_font_size.is_some_and(|old| old != new_font_size)
1009 || old_line_height.is_some_and(|old| old != new_line_height))
1010 {
1011 child_restyle_hint |= RestyleHint::restyle_subtree();
1018 }
1019 }
1020
1021 if context.shared.stylist.quirks_mode() == QuirksMode::Quirks {
1022 if self.is_html_document_body_element() {
1023 let device = context.shared.stylist.device();
1028
1029 let text_color = new_primary_style.get_inherited_text().clone_color();
1031 device.set_body_text_color(text_color);
1032 }
1033 }
1034
1035 if context
1037 .shared
1038 .traversal_flags
1039 .contains(TraversalFlags::FinalAnimationTraversal)
1040 {
1041 return RestyleHint::RECASCADE_SELF;
1042 }
1043
1044 let old_primary_style = match old_styles.primary {
1046 Some(s) => s,
1047 None => return RestyleHint::RECASCADE_SELF,
1048 };
1049
1050 let old_container_type = old_primary_style.clone_container_type();
1051 let new_container_type = new_primary_style.clone_container_type();
1052 if old_container_type != new_container_type && !new_container_type.is_size_container_type()
1053 {
1054 child_restyle_hint |= RestyleHint::restyle_subtree();
1057 } else if old_container_type.is_size_container_type()
1058 && !old_primary_style.is_display_contents()
1059 && new_primary_style.is_display_contents()
1060 {
1061 child_restyle_hint |= RestyleHint::restyle_subtree();
1064 }
1065
1066 child_restyle_hint |= self.accumulate_damage_for(
1067 context.shared,
1068 &mut data.damage,
1069 &old_primary_style,
1070 new_primary_style,
1071 None,
1072 );
1073
1074 if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
1075 return child_restyle_hint;
1077 }
1078
1079 let pseudo_styles = old_styles
1080 .pseudos
1081 .as_array()
1082 .iter()
1083 .zip(data.styles.pseudos.as_array().iter());
1084
1085 for (i, (old, new)) in pseudo_styles.enumerate() {
1086 match (old, new) {
1087 (&Some(ref old), &Some(ref new)) => {
1088 self.accumulate_damage_for(
1089 context.shared,
1090 &mut data.damage,
1091 old,
1092 new,
1093 Some(&PseudoElement::from_eager_index(i)),
1094 );
1095 },
1096 (&None, &None) => {},
1097 _ => {
1098 let pseudo = PseudoElement::from_eager_index(i);
1103 let new_pseudo_should_exist =
1104 new.as_ref().map_or(false, |s| pseudo.should_exist(s));
1105 let old_pseudo_should_exist =
1106 old.as_ref().map_or(false, |s| pseudo.should_exist(s));
1107 if new_pseudo_should_exist != old_pseudo_should_exist {
1108 data.damage |= RestyleDamage::reconstruct();
1109 return child_restyle_hint;
1110 }
1111 },
1112 }
1113 }
1114
1115 child_restyle_hint
1116 }
1117
1118 fn replace_rules(
1123 &self,
1124 replacements: RestyleHint,
1125 context: &mut StyleContext<Self>,
1126 cascade_inputs: &mut ElementCascadeInputs,
1127 ) -> bool {
1128 let mut result = false;
1129 result |= self.replace_rules_internal(
1130 replacements,
1131 context,
1132 CascadeVisitedMode::Unvisited,
1133 cascade_inputs,
1134 );
1135 result |= self.replace_rules_internal(
1136 replacements,
1137 context,
1138 CascadeVisitedMode::Visited,
1139 cascade_inputs,
1140 );
1141 result
1142 }
1143
1144 fn compute_style_difference(
1148 &self,
1149 old_values: &ComputedValues,
1150 new_values: &ComputedValues,
1151 pseudo: Option<&PseudoElement>,
1152 ) -> StyleDifference {
1153 debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
1154 #[cfg(feature = "gecko")]
1155 {
1156 RestyleDamage::compute_style_difference(old_values, new_values)
1157 }
1158 #[cfg(feature = "servo")]
1159 {
1160 RestyleDamage::compute_style_difference::<Self>(old_values, new_values)
1161 }
1162 }
1163}
1164
1165impl<E: TElement> MatchMethods for E {}