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;
23#[cfg(feature = "servo")]
24use crate::rule_tree::RuleCascadeFlags;
25use crate::rule_tree::{CascadeLevel, CascadeOrigin, StrongRuleNode};
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 crate::values::generics::animation::GenericAnimationTimeline;
34use crate::values::specified::animation::Scroller;
35use servo_arc::{Arc, ArcBorrow};
36
37#[derive(Debug)]
39pub struct StyleDifference {
40 pub damage: RestyleDamage,
42 pub change: StyleChange,
44}
45
46#[derive(Clone, Copy, Debug)]
48pub enum StyleChange {
49 Unchanged,
51 Changed {
53 reset_only: bool,
55 custom_properties_changed: bool,
57 },
58}
59
60#[derive(Clone, Copy, Debug, Eq, PartialEq)]
62enum CascadeVisitedMode {
63 Unvisited,
65 Visited,
69}
70
71trait PrivateMatchMethods: TElement {
72 fn replace_single_rule_node(
73 context: &SharedStyleContext,
74 level: CascadeLevel,
75 layer_order: LayerOrder,
76 pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
77 path: &mut StrongRuleNode,
78 ) -> bool {
79 let stylist = &context.stylist;
80 let guards = &context.guards;
81
82 let mut important_rules_changed = false;
83 let new_node = stylist.rule_tree().update_rule_at_level(
84 level,
85 layer_order,
86 pdb,
87 path,
88 guards,
89 &mut important_rules_changed,
90 );
91 if let Some(n) = new_node {
92 *path = n;
93 }
94 important_rules_changed
95 }
96
97 fn replace_rules_internal(
102 &self,
103 replacements: RestyleHint,
104 context: &mut StyleContext<Self>,
105 cascade_visited: CascadeVisitedMode,
106 cascade_inputs: &mut ElementCascadeInputs,
107 ) -> bool {
108 debug_assert!(
109 replacements.intersects(RestyleHint::replacements())
110 && (replacements & !RestyleHint::replacements()).is_empty()
111 );
112
113 let primary_rules = match cascade_visited {
114 CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(),
115 CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(),
116 };
117
118 let primary_rules = match primary_rules {
119 Some(r) => r,
120 None => return false,
121 };
122
123 if !context.shared.traversal_flags.for_animation_only() {
124 let mut result = false;
125 if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
126 let style_attribute = self.style_attribute();
127 result |= Self::replace_single_rule_node(
128 context.shared,
129 CascadeLevel::same_tree_author_normal(),
130 LayerOrder::style_attribute(),
131 style_attribute,
132 primary_rules,
133 );
134 result |= Self::replace_single_rule_node(
135 context.shared,
136 CascadeLevel::same_tree_author_important(),
137 LayerOrder::style_attribute(),
138 style_attribute,
139 primary_rules,
140 );
141 }
142 return result;
143 }
144
145 if replacements.intersects(RestyleHint::for_animations()) {
151 debug_assert!(context.shared.traversal_flags.for_animation_only());
152
153 if replacements.contains(RestyleHint::RESTYLE_SMIL) {
154 Self::replace_single_rule_node(
155 context.shared,
156 CascadeLevel::new(CascadeOrigin::SMILOverride),
157 LayerOrder::root(),
158 self.smil_override(),
159 primary_rules,
160 );
161 }
162
163 if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
164 Self::replace_single_rule_node(
165 context.shared,
166 CascadeLevel::new(CascadeOrigin::Transitions),
167 LayerOrder::root(),
168 self.transition_rule(&context.shared)
169 .as_ref()
170 .map(|a| a.borrow_arc()),
171 primary_rules,
172 );
173 }
174
175 if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
176 Self::replace_single_rule_node(
177 context.shared,
178 CascadeLevel::new(CascadeOrigin::Animations),
179 LayerOrder::root(),
180 self.animation_rule(&context.shared)
181 .as_ref()
182 .map(|a| a.borrow_arc()),
183 primary_rules,
184 );
185 }
186 }
187
188 false
189 }
190
191 #[inline]
192 fn requires_animation_update_for_scroll_self(
193 old: &ComputedValues,
194 new: &ComputedValues,
195 ) -> bool {
196 let scrollable_changed = old.clone_overflow_x().is_scrollable()
203 != new.clone_overflow_x().is_scrollable()
204 || old.clone_overflow_y().is_scrollable() != new.clone_overflow_y().is_scrollable();
205 if !scrollable_changed {
206 return false;
207 }
208 new.get_ui().animation_timeline_iter().any(|timeline| {
209 let scroll_function = match timeline {
210 GenericAnimationTimeline::Scroll(ref sf) => sf,
211 _ => return false,
212 };
213 if scroll_function.scroller != Scroller::SelfElement {
214 return false;
215 }
216 true
217 })
218 }
219
220 fn after_change_style(
222 &self,
223 context: &mut StyleContext<Self>,
224 primary_style: &Arc<ComputedValues>,
225 ) -> Option<Arc<ComputedValues>> {
226 StyleResolverForElement::new(
228 *self,
229 context,
230 RuleInclusion::All,
231 PseudoElementResolution::IfApplicable,
232 )
233 .after_change_style(primary_style)
234 }
235
236 fn needs_animations_update(
237 &self,
238 context: &mut StyleContext<Self>,
239 old_style: Option<&ComputedValues>,
240 new_style: &ComputedValues,
241 pseudo_element: Option<PseudoElement>,
242 ) -> bool {
243 let new_ui_style = new_style.get_ui();
244 let new_style_specifies_animations = new_ui_style.specifies_animations();
245
246 let has_animations = self.has_css_animations(&context.shared, pseudo_element);
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 None => {
267 return new_style_specifies_animations || new_style.is_pseudo_style();
268 },
269 };
270
271 let old_ui_style = old_style.get_ui();
272
273 let keyframes_could_have_changed = context
274 .shared
275 .traversal_flags
276 .contains(TraversalFlags::ForCSSRuleChanges);
277
278 if keyframes_could_have_changed {
286 return true;
287 }
288
289 if !old_ui_style.animations_equals(new_ui_style) {
291 return true;
292 }
293
294 let old_display = old_style.clone_display();
295 let new_display = new_style.clone_display();
296
297 if old_display == Display::None && new_display != Display::None {
299 return new_style_specifies_animations;
300 }
301
302 if old_display != Display::None && new_display == Display::None {
304 return has_animations;
305 }
306
307 if new_style.writing_mode != old_style.writing_mode {
312 return has_animations;
313 }
314
315 if Self::requires_animation_update_for_scroll_self(old_style, new_style) {
316 return has_animations;
317 }
318
319 false
320 }
321
322 fn might_need_transitions_update(
323 &self,
324 context: &StyleContext<Self>,
325 old_style: Option<&ComputedValues>,
326 new_style: &ComputedValues,
327 pseudo_element: Option<PseudoElement>,
328 ) -> bool {
329 let old_style = match old_style {
330 Some(v) => v,
331 None => return false,
332 };
333
334 if !self.has_css_transitions(context.shared, pseudo_element)
335 && !new_style.get_ui().specifies_transitions()
336 {
337 return false;
338 }
339
340 if old_style.clone_display().is_none() {
341 return false;
342 }
343
344 return true;
345 }
346
347 #[cfg(feature = "gecko")]
348 fn maybe_resolve_starting_style(
349 &self,
350 context: &mut StyleContext<Self>,
351 old_values: Option<&Arc<ComputedValues>>,
352 new_styles: &ResolvedElementStyles,
353 ) -> Option<Arc<ComputedValues>> {
354 let new_primary = new_styles.primary_style();
357 if !new_primary.get_ui().specifies_transitions() {
358 return None;
359 }
360
361 if old_values.is_some()
364 && !new_primary.is_display_property_changed_from_none(old_values.map(|s| &**s))
365 {
366 return None;
367 }
368
369 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(new_primary)?;
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 = starting_values.as_ref().or(old_values);
399 let new_values = new_styles.primary_style_mut();
400
401 if !self.might_need_transitions_update(
402 context,
403 before_change_or_starting.map(|s| &**s),
404 new_values,
405 None,
406 ) {
407 return None;
408 }
409
410 let after_change_style =
411 if self.has_css_transitions(context.shared, None) {
412 self.after_change_style(context, new_values)
413 } else {
414 None
415 };
416
417 if !self.needs_transitions_update(
421 before_change_or_starting.unwrap(),
422 after_change_style.as_ref().unwrap_or(&new_values),
423 ) {
424 return None;
425 }
426
427 if let Some(values_without_transitions) = after_change_style {
428 *new_values = values_without_transitions;
429 }
430
431 if starting_values.is_some() {
433 starting_values
434 } else {
435 old_values.cloned()
436 }
437 }
438
439 #[cfg(feature = "gecko")]
440 fn process_animations(
441 &self,
442 context: &mut StyleContext<Self>,
443 old_styles: &mut ElementStyles,
444 new_styles: &mut ResolvedElementStyles,
445 important_rules_changed: bool,
446 ) {
447 use crate::context::UpdateAnimationsTasks;
448
449 let old_values = &old_styles.primary;
450 if context.shared.traversal_flags.for_animation_only() && old_values.is_some() {
451 return;
452 }
453
454 let mut tasks = UpdateAnimationsTasks::empty();
458
459 if old_values.as_deref().map_or_else(
460 || {
461 new_styles
462 .primary_style()
463 .get_ui()
464 .specifies_timeline_scope()
465 },
466 |old| {
467 !old.get_ui()
468 .timeline_scope_equals(new_styles.primary_style().get_ui())
469 },
470 ) {
471 tasks.insert(UpdateAnimationsTasks::TIMELINE_SCOPES);
472 }
473
474 if old_values.as_deref().map_or_else(
475 || {
476 new_styles
477 .primary_style()
478 .get_ui()
479 .specifies_scroll_timelines()
480 },
481 |old| {
482 !old.get_ui()
483 .scroll_timelines_equals(new_styles.primary_style().get_ui())
484 },
485 ) {
486 tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES);
487 }
488
489 if old_values.as_deref().map_or_else(
490 || {
491 new_styles
492 .primary_style()
493 .get_ui()
494 .specifies_view_timelines()
495 },
496 |old| {
497 !old.get_ui()
498 .view_timelines_equals(new_styles.primary_style().get_ui())
499 },
500 ) {
501 tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES);
502 }
503
504 if self.needs_animations_update(
505 context,
506 old_values.as_deref(),
507 new_styles.primary_style(),
508 None,
509 ) {
510 tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
511 }
512
513 let before_change_style =
514 self.process_transitions(context, old_values.as_ref(), new_styles);
515 if before_change_style.is_some() {
516 tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS);
517 }
518
519 if self.has_animations(&context.shared) {
520 tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES);
521 if important_rules_changed {
522 tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);
523 }
524 if new_styles
525 .primary_style()
526 .is_display_property_changed_from_none(old_values.as_deref())
527 {
528 tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);
529 }
530 }
531
532 if !tasks.is_empty() {
533 let task = crate::context::SequentialTask::update_animations(
534 *self,
535 before_change_style,
536 tasks,
537 );
538 context.thread_local.tasks.push(task);
539 }
540 }
541
542 #[cfg(feature = "servo")]
543 fn process_animations(
544 &self,
545 context: &mut StyleContext<Self>,
546 old_styles: &mut ElementStyles,
547 new_resolved_styles: &mut ResolvedElementStyles,
548 _important_rules_changed: bool,
549 ) {
550 use crate::animation::AnimationSetKey;
551 use crate::dom::TDocument;
552
553 let style_changed = self.process_animations_for_style(
554 context,
555 &mut old_styles.primary,
556 new_resolved_styles.primary_style_mut(),
557 None,
558 );
559
560 if style_changed {
562 let primary_style = new_resolved_styles.primary_style();
563 let mut rule_node = primary_style.rules().clone();
564 let declarations = context.shared.animations.get_all_declarations(
565 &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()),
566 context.shared.current_time_for_animations,
567 self.as_node().owner_doc().shared_lock(),
568 );
569 Self::replace_single_rule_node(
570 &context.shared,
571 CascadeLevel::new(CascadeOrigin::Transitions),
572 LayerOrder::root(),
573 declarations.transitions.as_ref().map(|a| a.borrow_arc()),
574 &mut rule_node,
575 );
576 Self::replace_single_rule_node(
577 &context.shared,
578 CascadeLevel::new(CascadeOrigin::Animations),
579 LayerOrder::root(),
580 declarations.animations.as_ref().map(|a| a.borrow_arc()),
581 &mut rule_node,
582 );
583
584 if rule_node != *primary_style.rules() {
585 let inputs = CascadeInputs {
586 rules: Some(rule_node),
587 visited_rules: primary_style.visited_rules().cloned(),
588 flags: primary_style.flags.for_cascade_inputs(),
589 included_cascade_flags: RuleCascadeFlags::empty(),
590 };
591
592 new_resolved_styles.primary.style = StyleResolverForElement::new(
593 *self,
594 context,
595 RuleInclusion::All,
596 PseudoElementResolution::IfApplicable,
597 )
598 .cascade_style_and_visited_with_default_parents(inputs);
599 }
600 }
601
602 self.process_animations_for_pseudo(
603 context,
604 old_styles,
605 new_resolved_styles,
606 PseudoElement::Before,
607 );
608 self.process_animations_for_pseudo(
609 context,
610 old_styles,
611 new_resolved_styles,
612 PseudoElement::After,
613 );
614 }
615
616 #[cfg(feature = "servo")]
617 fn process_animations_for_pseudo(
618 &self,
619 context: &mut StyleContext<Self>,
620 old_styles: &ElementStyles,
621 new_resolved_styles: &mut ResolvedElementStyles,
622 pseudo_element: PseudoElement,
623 ) {
624 use crate::animation::AnimationSetKey;
625 use crate::dom::TDocument;
626
627 let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone());
628 let style = match new_resolved_styles.pseudos.get(&pseudo_element) {
629 Some(style) => Arc::clone(style),
630 None => {
631 context
632 .shared
633 .animations
634 .cancel_all_animations_for_key(&key);
635 return;
636 },
637 };
638
639 let old_style = old_styles.pseudos.get(&pseudo_element).cloned();
640 self.process_animations_for_style(
641 context,
642 &old_style,
643 &style,
644 Some(pseudo_element.clone()),
645 );
646
647 let declarations = context.shared.animations.get_all_declarations(
648 &key,
649 context.shared.current_time_for_animations,
650 self.as_node().owner_doc().shared_lock(),
651 );
652 if declarations.is_empty() {
653 return;
654 }
655
656 let mut rule_node = style.rules().clone();
657 Self::replace_single_rule_node(
658 &context.shared,
659 CascadeLevel::new(CascadeOrigin::Transitions),
660 LayerOrder::root(),
661 declarations.transitions.as_ref().map(|a| a.borrow_arc()),
662 &mut rule_node,
663 );
664 Self::replace_single_rule_node(
665 &context.shared,
666 CascadeLevel::new(CascadeOrigin::Animations),
667 LayerOrder::root(),
668 declarations.animations.as_ref().map(|a| a.borrow_arc()),
669 &mut rule_node,
670 );
671 if rule_node == *style.rules() {
672 return;
673 }
674
675 let inputs = CascadeInputs {
676 rules: Some(rule_node),
677 visited_rules: style.visited_rules().cloned(),
678 flags: style.flags.for_cascade_inputs(),
679 included_cascade_flags: RuleCascadeFlags::empty(),
680 };
681
682 let new_style = StyleResolverForElement::new(
683 *self,
684 context,
685 RuleInclusion::All,
686 PseudoElementResolution::IfApplicable,
687 )
688 .cascade_style_and_visited_for_pseudo_with_default_parents(
689 inputs,
690 &pseudo_element,
691 &new_resolved_styles.primary,
692 );
693
694 new_resolved_styles
695 .pseudos
696 .set(&pseudo_element, new_style.0);
697 }
698
699 #[cfg(feature = "servo")]
700 fn process_animations_for_style(
701 &self,
702 context: &mut StyleContext<Self>,
703 old_values: &Option<Arc<ComputedValues>>,
704 new_values: &Arc<ComputedValues>,
705 pseudo_element: Option<PseudoElement>,
706 ) -> bool {
707 use crate::animation::{AnimationSetKey, AnimationState};
708
709 let needs_animations_update = self.needs_animations_update(
712 context,
713 old_values.as_deref(),
714 new_values,
715 pseudo_element,
716 );
717
718 let might_need_transitions_update = self.might_need_transitions_update(
719 context,
720 old_values.as_deref(),
721 new_values,
722 pseudo_element,
723 );
724
725 let mut after_change_style = None;
726 if might_need_transitions_update {
727 after_change_style = self.after_change_style(context, new_values);
728 }
729
730 let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
731 let shared_context = context.shared;
732 let mut animation_set = shared_context
733 .animations
734 .sets
735 .write()
736 .remove(&key)
737 .unwrap_or_default();
738
739 if needs_animations_update {
743 let mut resolver = StyleResolverForElement::new(
744 *self,
745 context,
746 RuleInclusion::All,
747 PseudoElementResolution::IfApplicable,
748 );
749
750 animation_set.update_animations_for_new_style::<Self>(
751 *self,
752 &shared_context,
753 &new_values,
754 &mut resolver,
755 );
756 }
757
758 animation_set.update_transitions_for_new_style(
759 might_need_transitions_update,
760 &shared_context,
761 old_values.as_ref(),
762 after_change_style.as_ref().unwrap_or(new_values),
763 );
764
765 animation_set
768 .transitions
769 .retain(|transition| transition.state != AnimationState::Finished);
770
771 animation_set
772 .animations
773 .retain(|animation| animation.state != AnimationState::Finished);
774
775 let changed_animations = animation_set.dirty;
778 if !animation_set.is_empty() {
779 animation_set.dirty = false;
780 shared_context
781 .animations
782 .sets
783 .write()
784 .insert(key, animation_set);
785 }
786
787 changed_animations
788 }
789
790 fn accumulate_damage_for(
792 &self,
793 shared_context: &SharedStyleContext,
794 damage: &mut RestyleDamage,
795 old_values: &ComputedValues,
796 new_values: &ComputedValues,
797 pseudo: Option<&PseudoElement>,
798 ) -> RestyleHint {
799 debug!("accumulate_damage_for: {:?}", self);
800 debug_assert!(!shared_context
801 .traversal_flags
802 .contains(TraversalFlags::FinalAnimationTraversal));
803
804 let difference = self.compute_style_difference(old_values, new_values, pseudo);
805
806 *damage |= difference.damage;
807
808 debug!(" > style difference: {:?}", difference);
809
810 let mut children_hint = RestyleHint::empty();
811 if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() {
812 debug!(
815 " > flags changed: {:?} != {:?}",
816 old_values.flags, new_values.flags
817 );
818 children_hint |= RestyleHint::RECASCADE_SELF;
819 } else if old_values.effective_zoom != new_values.effective_zoom {
820 debug!(
822 " > zoom changed: {:?} != {:?}",
823 old_values.effective_zoom, new_values.effective_zoom
824 );
825 children_hint |= RestyleHint::RECASCADE_SELF;
826 }
827
828 let StyleChange::Changed {
829 reset_only,
830 custom_properties_changed,
831 } = difference.change
832 else {
833 return children_hint;
834 };
835
836 let new_container_name = new_values.clone_container_name();
837 if new_container_name != old_values.clone_container_name() {
838 children_hint |= RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER;
841 } else if custom_properties_changed {
842 children_hint |= if !new_container_name.is_none() {
845 RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER
846 } else {
847 RestyleHint::RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES
848 };
849 }
850
851 if reset_only {
852 children_hint |=
856 if need_to_unconditionally_recascade_for_reset_change(old_values, new_values) {
857 RestyleHint::RECASCADE_SELF
858 } else {
859 RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE
860 };
861 } else {
862 children_hint |= RestyleHint::RECASCADE_SELF;
864 }
865
866 children_hint
867 }
868}
869
870fn need_to_unconditionally_recascade_for_reset_change(
872 old_values: &ComputedValues,
873 new_values: &ComputedValues,
874) -> bool {
875 let old_display = old_values.clone_display();
876 let new_display = new_values.clone_display();
877
878 if old_display != new_display {
879 if old_display == Display::None {
882 return true;
883 }
884 if old_display.is_item_container() != new_display.is_item_container() {
887 return true;
888 }
889 if old_display.is_contents() || new_display.is_contents() {
892 return true;
893 }
894 #[cfg(feature = "gecko")]
897 if old_display.is_ruby_type() != new_display.is_ruby_type() {
898 return true;
899 }
900 }
901
902 #[cfg(feature = "gecko")]
908 {
909 use crate::values::specified::align::AlignFlags;
910
911 let old_justify_items = old_values.get_position().clone_justify_items();
912 let new_justify_items = new_values.get_position().clone_justify_items();
913
914 let was_legacy_justify_items = old_justify_items.computed.contains(AlignFlags::LEGACY);
915
916 let is_legacy_justify_items = new_justify_items.computed.contains(AlignFlags::LEGACY);
917
918 if is_legacy_justify_items != was_legacy_justify_items {
919 return true;
920 }
921
922 if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed {
923 return true;
924 }
925 }
926
927 false
928}
929
930impl<E: TElement> PrivateMatchMethods for E {}
931
932pub trait MatchMethods: TElement {
934 fn layout_parent(&self) -> Self {
945 let mut current = self.clone();
946 loop {
947 current = match current.traversal_parent() {
948 Some(el) => el,
949 None => return current,
950 };
951
952 let is_display_contents = current
953 .borrow_data()
954 .unwrap()
955 .styles
956 .primary()
957 .is_display_contents();
958
959 if !is_display_contents {
960 return current;
961 }
962 }
963 }
964
965 fn finish_restyle(
968 &self,
969 context: &mut StyleContext<Self>,
970 data: &mut ElementData,
971 mut new_styles: ResolvedElementStyles,
972 important_rules_changed: bool,
973 ) -> RestyleHint {
974 self.process_animations(
975 context,
976 &mut data.styles,
977 &mut new_styles,
978 important_rules_changed,
979 );
980
981 let old_styles = data.set_styles(new_styles);
983
984 let new_primary_style = data.styles.primary.as_ref().unwrap();
985
986 let mut child_restyle_hint = RestyleHint::empty();
987 let is_root = new_primary_style
988 .flags
989 .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
990
991 let device = context.shared.stylist.device();
992 let new_font_size = new_primary_style.get_font().clone_font_size();
993 let new_container_type = new_primary_style.clone_container_type();
994 let is_container = !new_container_type.is_normal();
995
996 let old_style = old_styles.primary.as_ref();
997 let old_font_size = old_style.map(|s| s.get_font().clone_font_size());
998 let (old_line_height, new_line_height) = if is_root || is_container {
999 (
1004 old_style.map(|s| {
1005 device
1006 .calc_line_height(&s.get_font(), s.writing_mode, None)
1007 .0
1008 }),
1009 Some(
1010 device
1011 .calc_line_height(
1012 &new_primary_style.get_font(),
1013 new_primary_style.writing_mode,
1014 None,
1015 )
1016 .0,
1017 ),
1018 )
1019 } else {
1020 (None, None)
1021 };
1022 let font_size_changed = old_font_size.is_none_or(|fs| fs != new_font_size);
1023 let line_height_changed = old_line_height != new_line_height;
1024 if is_root {
1027 debug_assert!(self.owner_doc_matches_for_testing(device));
1028 device.set_root_style(new_primary_style);
1029
1030 if font_size_changed {
1032 let size = new_font_size.computed_size();
1033 device.set_root_font_size(new_primary_style.effective_zoom.unzoom(size.px()));
1034 }
1035
1036 if line_height_changed {
1038 device.set_root_line_height(
1039 new_primary_style
1040 .effective_zoom
1041 .unzoom(new_line_height.unwrap().px()),
1042 );
1043 }
1044
1045 if device.used_root_font_metrics() && device.update_root_font_metrics() {
1049 child_restyle_hint |= RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT;
1050 }
1051 }
1052
1053 if font_size_changed || line_height_changed {
1054 child_restyle_hint |= RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT;
1055 }
1056
1057 if context.shared.stylist.quirks_mode() == QuirksMode::Quirks {
1058 if self.is_html_document_body_element() {
1059 let device = context.shared.stylist.device();
1064
1065 let text_color = new_primary_style.get_inherited_text().clone_color();
1067 device.set_body_text_color(text_color);
1068 }
1069 }
1070
1071 if context
1073 .shared
1074 .traversal_flags
1075 .contains(TraversalFlags::FinalAnimationTraversal)
1076 {
1077 return RestyleHint::RECASCADE_SELF;
1078 }
1079
1080 let old_primary_style = match old_styles.primary {
1082 Some(s) => s,
1083 None => return RestyleHint::RECASCADE_SELF,
1084 };
1085
1086 let old_container_type = old_primary_style.clone_container_type();
1087 if old_container_type != new_container_type && !new_container_type.is_size_container_type()
1088 {
1089 child_restyle_hint |= RestyleHint::restyle_subtree();
1092 } else if old_container_type.is_size_container_type()
1093 && !old_primary_style.is_display_contents()
1094 && new_primary_style.is_display_contents()
1095 {
1096 child_restyle_hint |= RestyleHint::restyle_subtree();
1099 }
1100
1101 child_restyle_hint |= self.accumulate_damage_for(
1102 context.shared,
1103 &mut data.damage,
1104 &old_primary_style,
1105 new_primary_style,
1106 None,
1107 );
1108
1109 if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
1110 return child_restyle_hint;
1112 }
1113
1114 let pseudo_styles = old_styles
1115 .pseudos
1116 .as_array()
1117 .iter()
1118 .zip(data.styles.pseudos.as_array().iter());
1119
1120 for (i, (old, new)) in pseudo_styles.enumerate() {
1121 match (old, new) {
1122 (&Some(ref old), &Some(ref new)) => {
1123 self.accumulate_damage_for(
1124 context.shared,
1125 &mut data.damage,
1126 old,
1127 new,
1128 Some(&PseudoElement::from_eager_index(i)),
1129 );
1130 },
1131 (&None, &None) => {},
1132 _ => {
1133 let pseudo = PseudoElement::from_eager_index(i);
1138 let new_pseudo_should_exist =
1139 new.as_ref().map_or(false, |s| pseudo.should_exist(s));
1140 let old_pseudo_should_exist =
1141 old.as_ref().map_or(false, |s| pseudo.should_exist(s));
1142 if new_pseudo_should_exist != old_pseudo_should_exist {
1143 data.damage |= RestyleDamage::reconstruct();
1144 return child_restyle_hint;
1145 }
1146 },
1147 }
1148 }
1149
1150 child_restyle_hint
1151 }
1152
1153 fn replace_rules(
1158 &self,
1159 replacements: RestyleHint,
1160 context: &mut StyleContext<Self>,
1161 cascade_inputs: &mut ElementCascadeInputs,
1162 ) -> bool {
1163 let mut result = false;
1164 result |= self.replace_rules_internal(
1165 replacements,
1166 context,
1167 CascadeVisitedMode::Unvisited,
1168 cascade_inputs,
1169 );
1170 result |= self.replace_rules_internal(
1171 replacements,
1172 context,
1173 CascadeVisitedMode::Visited,
1174 cascade_inputs,
1175 );
1176 result
1177 }
1178
1179 fn compute_style_difference(
1183 &self,
1184 old_values: &ComputedValues,
1185 new_values: &ComputedValues,
1186 pseudo: Option<&PseudoElement>,
1187 ) -> StyleDifference {
1188 debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
1189 #[cfg(feature = "gecko")]
1190 {
1191 RestyleDamage::compute_style_difference(old_values, new_values)
1192 }
1193 #[cfg(feature = "servo")]
1194 {
1195 RestyleDamage::compute_style_difference::<Self>(old_values, new_values)
1196 }
1197 }
1198}
1199
1200impl<E: TElement> MatchMethods for E {}