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 self.unset_dirty_style_attribute();
143 }
144 return result;
145 }
146
147 if replacements.intersects(RestyleHint::for_animations()) {
153 debug_assert!(context.shared.traversal_flags.for_animation_only());
154
155 if replacements.contains(RestyleHint::RESTYLE_SMIL) {
156 Self::replace_single_rule_node(
157 context.shared,
158 CascadeLevel::new(CascadeOrigin::SMILOverride),
159 LayerOrder::root(),
160 self.smil_override(),
161 primary_rules,
162 );
163 }
164
165 if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
166 Self::replace_single_rule_node(
167 context.shared,
168 CascadeLevel::new(CascadeOrigin::Transitions),
169 LayerOrder::root(),
170 self.transition_rule(&context.shared)
171 .as_ref()
172 .map(|a| a.borrow_arc()),
173 primary_rules,
174 );
175 }
176
177 if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
178 Self::replace_single_rule_node(
179 context.shared,
180 CascadeLevel::new(CascadeOrigin::Animations),
181 LayerOrder::root(),
182 self.animation_rule(&context.shared)
183 .as_ref()
184 .map(|a| a.borrow_arc()),
185 primary_rules,
186 );
187 }
188 }
189
190 false
191 }
192
193 #[inline]
194 fn requires_animation_update_for_scroll_self(
195 old: &ComputedValues,
196 new: &ComputedValues,
197 ) -> bool {
198 let scrollable_changed = old.clone_overflow_x().is_scrollable()
205 != new.clone_overflow_x().is_scrollable()
206 || old.clone_overflow_y().is_scrollable() != new.clone_overflow_y().is_scrollable();
207 if !scrollable_changed {
208 return false;
209 }
210 new.get_ui().animation_timeline_iter().any(|timeline| {
211 let scroll_function = match timeline {
212 GenericAnimationTimeline::Scroll(ref sf) => sf,
213 _ => return false,
214 };
215 if scroll_function.scroller != Scroller::SelfElement {
216 return false;
217 }
218 true
219 })
220 }
221
222 fn after_change_style(
224 &self,
225 context: &mut StyleContext<Self>,
226 primary_style: &Arc<ComputedValues>,
227 ) -> Option<Arc<ComputedValues>> {
228 StyleResolverForElement::new(
230 *self,
231 context,
232 RuleInclusion::All,
233 PseudoElementResolution::IfApplicable,
234 )
235 .after_change_style(primary_style)
236 }
237
238 fn needs_animations_update(
239 &self,
240 context: &mut StyleContext<Self>,
241 old_style: Option<&ComputedValues>,
242 new_style: &ComputedValues,
243 pseudo_element: Option<PseudoElement>,
244 ) -> bool {
245 let new_ui_style = new_style.get_ui();
246 let new_style_specifies_animations = new_ui_style.specifies_animations();
247
248 let has_animations = self.has_css_animations(&context.shared, pseudo_element);
249 if !new_style_specifies_animations && !has_animations {
250 return false;
251 }
252
253 let old_style = match old_style {
254 Some(old) => old,
255 None => {
269 return new_style_specifies_animations || new_style.is_pseudo_style();
270 },
271 };
272
273 let old_ui_style = old_style.get_ui();
274
275 let keyframes_could_have_changed = context
276 .shared
277 .traversal_flags
278 .contains(TraversalFlags::ForCSSRuleChanges);
279
280 if keyframes_could_have_changed {
288 return true;
289 }
290
291 if !old_ui_style.animations_equals(new_ui_style) {
293 return true;
294 }
295
296 let old_display = old_style.clone_display();
297 let new_display = new_style.clone_display();
298
299 if old_display == Display::None && new_display != Display::None {
301 return new_style_specifies_animations;
302 }
303
304 if old_display != Display::None && new_display == Display::None {
306 return has_animations;
307 }
308
309 if new_style.writing_mode != old_style.writing_mode {
314 return has_animations;
315 }
316
317 if Self::requires_animation_update_for_scroll_self(old_style, new_style) {
318 return has_animations;
319 }
320
321 false
322 }
323
324 fn might_need_transitions_update(
325 &self,
326 context: &StyleContext<Self>,
327 old_style: Option<&ComputedValues>,
328 new_style: &ComputedValues,
329 pseudo_element: Option<PseudoElement>,
330 ) -> bool {
331 let old_style = match old_style {
332 Some(v) => v,
333 None => return false,
334 };
335
336 if !self.has_css_transitions(context.shared, pseudo_element)
337 && !new_style.get_ui().specifies_transitions()
338 {
339 return false;
340 }
341
342 if old_style.clone_display().is_none() {
343 return false;
344 }
345
346 return true;
347 }
348
349 #[cfg(feature = "gecko")]
350 fn maybe_resolve_starting_style(
351 &self,
352 context: &mut StyleContext<Self>,
353 old_values: Option<&Arc<ComputedValues>>,
354 new_styles: &ResolvedElementStyles,
355 ) -> Option<Arc<ComputedValues>> {
356 let new_primary = new_styles.primary_style();
359 if !new_primary.get_ui().specifies_transitions() {
360 return None;
361 }
362
363 if old_values.is_some()
366 && !new_primary.is_display_property_changed_from_none(old_values.map(|s| &**s))
367 {
368 return None;
369 }
370
371 let mut resolver = StyleResolverForElement::new(
372 *self,
373 context,
374 RuleInclusion::All,
375 PseudoElementResolution::IfApplicable,
376 );
377
378 let starting_style = resolver.resolve_starting_style(new_primary)?;
379 if starting_style.style().clone_display().is_none() {
380 return None;
381 }
382
383 Some(starting_style.0)
384 }
385
386 #[cfg(feature = "gecko")]
393 fn process_transitions(
394 &self,
395 context: &mut StyleContext<Self>,
396 old_values: Option<&Arc<ComputedValues>>,
397 new_styles: &mut ResolvedElementStyles,
398 ) -> Option<Arc<ComputedValues>> {
399 let starting_values = self.maybe_resolve_starting_style(context, old_values, new_styles);
400 let before_change_or_starting = starting_values.as_ref().or(old_values);
401 let new_values = new_styles.primary_style_mut();
402
403 if !self.might_need_transitions_update(
404 context,
405 before_change_or_starting.map(|s| &**s),
406 new_values,
407 None,
408 ) {
409 return None;
410 }
411
412 let after_change_style =
413 if self.has_css_transitions(context.shared, None) {
414 self.after_change_style(context, new_values)
415 } else {
416 None
417 };
418
419 if !self.needs_transitions_update(
423 before_change_or_starting.unwrap(),
424 after_change_style.as_ref().unwrap_or(&new_values),
425 ) {
426 return None;
427 }
428
429 if let Some(values_without_transitions) = after_change_style {
430 *new_values = values_without_transitions;
431 }
432
433 if starting_values.is_some() {
435 starting_values
436 } else {
437 old_values.cloned()
438 }
439 }
440
441 #[cfg(feature = "gecko")]
442 fn process_animations(
443 &self,
444 context: &mut StyleContext<Self>,
445 old_styles: &mut ElementStyles,
446 new_styles: &mut ResolvedElementStyles,
447 important_rules_changed: bool,
448 ) {
449 use crate::context::UpdateAnimationsTasks;
450
451 let old_values = &old_styles.primary;
452 if context.shared.traversal_flags.for_animation_only() && old_values.is_some() {
453 return;
454 }
455
456 let mut tasks = UpdateAnimationsTasks::empty();
460
461 if old_values.as_deref().map_or_else(
462 || {
463 new_styles
464 .primary_style()
465 .get_ui()
466 .specifies_timeline_scope()
467 },
468 |old| {
469 !old.get_ui()
470 .timeline_scope_equals(new_styles.primary_style().get_ui())
471 },
472 ) {
473 tasks.insert(UpdateAnimationsTasks::TIMELINE_SCOPES);
474 }
475
476 if old_values.as_deref().map_or_else(
477 || {
478 new_styles
479 .primary_style()
480 .get_ui()
481 .specifies_scroll_timelines()
482 },
483 |old| {
484 !old.get_ui()
485 .scroll_timelines_equals(new_styles.primary_style().get_ui())
486 },
487 ) {
488 tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES);
489 }
490
491 if old_values.as_deref().map_or_else(
492 || {
493 new_styles
494 .primary_style()
495 .get_ui()
496 .specifies_view_timelines()
497 },
498 |old| {
499 !old.get_ui()
500 .view_timelines_equals(new_styles.primary_style().get_ui())
501 },
502 ) {
503 tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES);
504 }
505
506 if self.needs_animations_update(
507 context,
508 old_values.as_deref(),
509 new_styles.primary_style(),
510 None,
511 ) {
512 tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
513 }
514
515 let before_change_style =
516 self.process_transitions(context, old_values.as_ref(), new_styles);
517 if before_change_style.is_some() {
518 tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS);
519 }
520
521 if self.has_animations(&context.shared) {
522 tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES);
523 if important_rules_changed {
524 tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);
525 }
526 if new_styles
527 .primary_style()
528 .is_display_property_changed_from_none(old_values.as_deref())
529 {
530 tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);
531 }
532 }
533
534 if !tasks.is_empty() {
535 let task = crate::context::SequentialTask::update_animations(
536 *self,
537 before_change_style,
538 tasks,
539 );
540 context.thread_local.tasks.push(task);
541 }
542 }
543
544 #[cfg(feature = "servo")]
545 fn process_animations(
546 &self,
547 context: &mut StyleContext<Self>,
548 old_styles: &mut ElementStyles,
549 new_resolved_styles: &mut ResolvedElementStyles,
550 _important_rules_changed: bool,
551 ) {
552 use crate::animation::AnimationSetKey;
553 use crate::dom::TDocument;
554
555 let style_changed = self.process_animations_for_style(
556 context,
557 &mut old_styles.primary,
558 new_resolved_styles.primary_style_mut(),
559 None,
560 );
561
562 if style_changed {
564 let primary_style = new_resolved_styles.primary_style();
565 let mut rule_node = primary_style.rules().clone();
566 let declarations = context.shared.animations.get_all_declarations(
567 &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()),
568 context.shared.current_time_for_animations,
569 self.as_node().owner_doc().shared_lock(),
570 );
571 Self::replace_single_rule_node(
572 &context.shared,
573 CascadeLevel::new(CascadeOrigin::Transitions),
574 LayerOrder::root(),
575 declarations.transitions.as_ref().map(|a| a.borrow_arc()),
576 &mut rule_node,
577 );
578 Self::replace_single_rule_node(
579 &context.shared,
580 CascadeLevel::new(CascadeOrigin::Animations),
581 LayerOrder::root(),
582 declarations.animations.as_ref().map(|a| a.borrow_arc()),
583 &mut rule_node,
584 );
585
586 if rule_node != *primary_style.rules() {
587 let inputs = CascadeInputs {
588 rules: Some(rule_node),
589 visited_rules: primary_style.visited_rules().cloned(),
590 flags: primary_style.flags.for_cascade_inputs(),
591 included_cascade_flags: RuleCascadeFlags::empty(),
592 };
593
594 new_resolved_styles.primary.style = StyleResolverForElement::new(
595 *self,
596 context,
597 RuleInclusion::All,
598 PseudoElementResolution::IfApplicable,
599 )
600 .cascade_style_and_visited_with_default_parents(inputs);
601 }
602 }
603
604 self.process_animations_for_pseudo(
605 context,
606 old_styles,
607 new_resolved_styles,
608 PseudoElement::Before,
609 );
610 self.process_animations_for_pseudo(
611 context,
612 old_styles,
613 new_resolved_styles,
614 PseudoElement::After,
615 );
616 }
617
618 #[cfg(feature = "servo")]
619 fn process_animations_for_pseudo(
620 &self,
621 context: &mut StyleContext<Self>,
622 old_styles: &ElementStyles,
623 new_resolved_styles: &mut ResolvedElementStyles,
624 pseudo_element: PseudoElement,
625 ) {
626 use crate::animation::AnimationSetKey;
627 use crate::dom::TDocument;
628
629 let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone());
630 let style = match new_resolved_styles.pseudos.get(&pseudo_element) {
631 Some(style) => Arc::clone(style),
632 None => {
633 context
634 .shared
635 .animations
636 .cancel_all_animations_for_key(&key);
637 return;
638 },
639 };
640
641 let old_style = old_styles.pseudos.get(&pseudo_element).cloned();
642 self.process_animations_for_style(
643 context,
644 &old_style,
645 &style,
646 Some(pseudo_element.clone()),
647 );
648
649 let declarations = context.shared.animations.get_all_declarations(
650 &key,
651 context.shared.current_time_for_animations,
652 self.as_node().owner_doc().shared_lock(),
653 );
654 if declarations.is_empty() {
655 return;
656 }
657
658 let mut rule_node = style.rules().clone();
659 Self::replace_single_rule_node(
660 &context.shared,
661 CascadeLevel::new(CascadeOrigin::Transitions),
662 LayerOrder::root(),
663 declarations.transitions.as_ref().map(|a| a.borrow_arc()),
664 &mut rule_node,
665 );
666 Self::replace_single_rule_node(
667 &context.shared,
668 CascadeLevel::new(CascadeOrigin::Animations),
669 LayerOrder::root(),
670 declarations.animations.as_ref().map(|a| a.borrow_arc()),
671 &mut rule_node,
672 );
673 if rule_node == *style.rules() {
674 return;
675 }
676
677 let inputs = CascadeInputs {
678 rules: Some(rule_node),
679 visited_rules: style.visited_rules().cloned(),
680 flags: style.flags.for_cascade_inputs(),
681 included_cascade_flags: RuleCascadeFlags::empty(),
682 };
683
684 let new_style = StyleResolverForElement::new(
685 *self,
686 context,
687 RuleInclusion::All,
688 PseudoElementResolution::IfApplicable,
689 )
690 .cascade_style_and_visited_for_pseudo_with_default_parents(
691 inputs,
692 &pseudo_element,
693 &new_resolved_styles.primary,
694 );
695
696 new_resolved_styles
697 .pseudos
698 .set(&pseudo_element, new_style.0);
699 }
700
701 #[cfg(feature = "servo")]
702 fn process_animations_for_style(
703 &self,
704 context: &mut StyleContext<Self>,
705 old_values: &Option<Arc<ComputedValues>>,
706 new_values: &Arc<ComputedValues>,
707 pseudo_element: Option<PseudoElement>,
708 ) -> bool {
709 use crate::animation::{AnimationSetKey, AnimationState};
710
711 let needs_animations_update = self.needs_animations_update(
714 context,
715 old_values.as_deref(),
716 new_values,
717 pseudo_element,
718 );
719
720 let might_need_transitions_update = self.might_need_transitions_update(
721 context,
722 old_values.as_deref(),
723 new_values,
724 pseudo_element,
725 );
726
727 let mut after_change_style = None;
728 if might_need_transitions_update {
729 after_change_style = self.after_change_style(context, new_values);
730 }
731
732 let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
733 let shared_context = context.shared;
734 let mut animation_set = shared_context
735 .animations
736 .sets
737 .write()
738 .remove(&key)
739 .unwrap_or_default();
740
741 if needs_animations_update {
745 let mut resolver = StyleResolverForElement::new(
746 *self,
747 context,
748 RuleInclusion::All,
749 PseudoElementResolution::IfApplicable,
750 );
751
752 animation_set.update_animations_for_new_style::<Self>(
753 *self,
754 &shared_context,
755 &new_values,
756 &mut resolver,
757 );
758 }
759
760 animation_set.update_transitions_for_new_style(
761 might_need_transitions_update,
762 &shared_context,
763 old_values.as_ref(),
764 after_change_style.as_ref().unwrap_or(new_values),
765 );
766
767 animation_set
770 .transitions
771 .retain(|transition| transition.state != AnimationState::Finished);
772
773 animation_set
774 .animations
775 .retain(|animation| animation.state != AnimationState::Finished);
776
777 let changed_animations = animation_set.dirty;
780 if !animation_set.is_empty() {
781 animation_set.dirty = false;
782 shared_context
783 .animations
784 .sets
785 .write()
786 .insert(key, animation_set);
787 }
788
789 changed_animations
790 }
791
792 fn accumulate_damage_for(
794 &self,
795 shared_context: &SharedStyleContext,
796 damage: &mut RestyleDamage,
797 old_values: &ComputedValues,
798 new_values: &ComputedValues,
799 pseudo: Option<&PseudoElement>,
800 ) -> RestyleHint {
801 debug!("accumulate_damage_for: {:?}", self);
802 debug_assert!(!shared_context
803 .traversal_flags
804 .contains(TraversalFlags::FinalAnimationTraversal));
805
806 let difference = self.compute_style_difference(old_values, new_values, pseudo);
807
808 *damage |= difference.damage;
809
810 debug!(" > style difference: {:?}", difference);
811
812 let mut children_hint = RestyleHint::empty();
813 if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() {
814 debug!(
817 " > flags changed: {:?} != {:?}",
818 old_values.flags, new_values.flags
819 );
820 children_hint |= RestyleHint::RECASCADE_SELF;
821 } else if old_values.effective_zoom != new_values.effective_zoom {
822 debug!(
824 " > zoom changed: {:?} != {:?}",
825 old_values.effective_zoom, new_values.effective_zoom
826 );
827 children_hint |= RestyleHint::RECASCADE_SELF;
828 }
829
830 let StyleChange::Changed {
831 reset_only,
832 custom_properties_changed,
833 } = difference.change
834 else {
835 return children_hint;
836 };
837
838 let new_container_name = new_values.clone_container_name();
839 if new_container_name != old_values.clone_container_name() {
840 children_hint |= RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER;
843 } else if custom_properties_changed {
844 children_hint |= if !new_container_name.is_none() {
847 RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER
848 } else {
849 RestyleHint::RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES
850 };
851 }
852
853 if reset_only {
854 children_hint |=
858 if need_to_unconditionally_recascade_for_reset_change(old_values, new_values) {
859 RestyleHint::RECASCADE_SELF
860 } else {
861 RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE
862 };
863 } else {
864 children_hint |= RestyleHint::RECASCADE_SELF;
866 }
867
868 children_hint
869 }
870}
871
872fn need_to_unconditionally_recascade_for_reset_change(
874 old_values: &ComputedValues,
875 new_values: &ComputedValues,
876) -> bool {
877 let old_display = old_values.clone_display();
878 let new_display = new_values.clone_display();
879
880 if old_display != new_display {
881 if old_display == Display::None {
884 return true;
885 }
886 if old_display.is_item_container() != new_display.is_item_container() {
889 return true;
890 }
891 if old_display.is_contents() || new_display.is_contents() {
894 return true;
895 }
896 #[cfg(feature = "gecko")]
899 if old_display.is_ruby_type() != new_display.is_ruby_type() {
900 return true;
901 }
902 }
903
904 #[cfg(feature = "gecko")]
910 {
911 use crate::values::specified::align::AlignFlags;
912
913 let old_justify_items = old_values.get_position().clone_justify_items();
914 let new_justify_items = new_values.get_position().clone_justify_items();
915
916 let was_legacy_justify_items = old_justify_items.computed.contains(AlignFlags::LEGACY);
917
918 let is_legacy_justify_items = new_justify_items.computed.contains(AlignFlags::LEGACY);
919
920 if is_legacy_justify_items != was_legacy_justify_items {
921 return true;
922 }
923
924 if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed {
925 return true;
926 }
927 }
928
929 false
930}
931
932impl<E: TElement> PrivateMatchMethods for E {}
933
934pub trait MatchMethods: TElement {
936 fn layout_parent(&self) -> Self {
947 let mut current = self.clone();
948 loop {
949 current = match current.traversal_parent() {
950 Some(el) => el,
951 None => return current,
952 };
953
954 let is_display_contents = current
955 .borrow_data()
956 .unwrap()
957 .styles
958 .primary()
959 .is_display_contents();
960
961 if !is_display_contents {
962 return current;
963 }
964 }
965 }
966
967 fn finish_restyle(
970 &self,
971 context: &mut StyleContext<Self>,
972 data: &mut ElementData,
973 mut new_styles: ResolvedElementStyles,
974 important_rules_changed: bool,
975 ) -> RestyleHint {
976 self.process_animations(
977 context,
978 &mut data.styles,
979 &mut new_styles,
980 important_rules_changed,
981 );
982
983 let old_styles = data.set_styles(new_styles);
985
986 let new_primary_style = data.styles.primary.as_ref().unwrap();
987
988 let mut child_restyle_hint = RestyleHint::empty();
989 let is_root = new_primary_style
990 .flags
991 .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
992
993 let old_style = old_styles.primary.as_ref();
994 let old_font_size = old_style.map(|s| s.get_font().clone_font_size());
995 let device = context.shared.stylist.device();
996 let new_font_size = new_primary_style.get_font().clone_font_size();
997 let new_line_height = device
1000 .calc_line_height(
1001 &new_primary_style.get_font(),
1002 new_primary_style.writing_mode,
1003 None,
1004 )
1005 .0;
1006 let old_line_height = old_style.map(|s| {
1007 device
1008 .calc_line_height(&s.get_font(), s.writing_mode, None)
1009 .0
1010 });
1011
1012 let font_size_changed = old_font_size != Some(new_font_size);
1013 let line_height_changed = old_line_height != Some(new_line_height);
1014
1015 if is_root {
1018 debug_assert!(self.owner_doc_matches_for_testing(device));
1019 device.set_root_style(new_primary_style);
1020
1021 if font_size_changed {
1023 let size = new_font_size.computed_size();
1024 device.set_root_font_size(new_primary_style.effective_zoom.unzoom(size.px()));
1025 if device.used_root_font_size() {
1026 child_restyle_hint |= RestyleHint::recascade_subtree();
1027 }
1028 }
1029
1030 if line_height_changed {
1032 device.set_root_line_height(
1033 new_primary_style
1034 .effective_zoom
1035 .unzoom(new_line_height.px()),
1036 );
1037 if device.used_root_line_height() {
1038 child_restyle_hint |= RestyleHint::recascade_subtree();
1039 }
1040 }
1041
1042 if device.used_root_font_metrics() && device.update_root_font_metrics() {
1046 child_restyle_hint |= RestyleHint::recascade_subtree()
1047 | RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT_METRICS;
1048 }
1049 }
1050
1051 if font_size_changed || line_height_changed {
1052 child_restyle_hint |= RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT_METRICS;
1053 }
1054
1055 if context.shared.stylist.quirks_mode() == QuirksMode::Quirks {
1056 if self.is_html_document_body_element() {
1057 let device = context.shared.stylist.device();
1062
1063 let text_color = new_primary_style.get_inherited_text().clone_color();
1065 device.set_body_text_color(text_color);
1066 }
1067 }
1068
1069 if context
1071 .shared
1072 .traversal_flags
1073 .contains(TraversalFlags::FinalAnimationTraversal)
1074 {
1075 return RestyleHint::RECASCADE_SELF;
1076 }
1077
1078 let old_primary_style = match old_styles.primary {
1080 Some(s) => s,
1081 None => return RestyleHint::RECASCADE_SELF,
1082 };
1083
1084 let old_container_type = old_primary_style.clone_container_type();
1085 let new_container_type = new_primary_style.clone_container_type();
1086 if old_container_type != new_container_type && !new_container_type.is_size_container_type()
1087 {
1088 child_restyle_hint |= RestyleHint::restyle_subtree();
1091 } else if old_container_type.is_size_container_type()
1092 && !old_primary_style.is_display_contents()
1093 && new_primary_style.is_display_contents()
1094 {
1095 child_restyle_hint |= RestyleHint::restyle_subtree();
1098 }
1099
1100 child_restyle_hint |= self.accumulate_damage_for(
1101 context.shared,
1102 &mut data.damage,
1103 &old_primary_style,
1104 new_primary_style,
1105 None,
1106 );
1107
1108 if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
1109 return child_restyle_hint;
1111 }
1112
1113 let pseudo_styles = old_styles
1114 .pseudos
1115 .as_array()
1116 .iter()
1117 .zip(data.styles.pseudos.as_array().iter());
1118
1119 for (i, (old, new)) in pseudo_styles.enumerate() {
1120 match (old, new) {
1121 (&Some(ref old), &Some(ref new)) => {
1122 self.accumulate_damage_for(
1123 context.shared,
1124 &mut data.damage,
1125 old,
1126 new,
1127 Some(&PseudoElement::from_eager_index(i)),
1128 );
1129 },
1130 (&None, &None) => {},
1131 _ => {
1132 let pseudo = PseudoElement::from_eager_index(i);
1137 let new_pseudo_should_exist =
1138 new.as_ref().map_or(false, |s| pseudo.should_exist(s));
1139 let old_pseudo_should_exist =
1140 old.as_ref().map_or(false, |s| pseudo.should_exist(s));
1141 if new_pseudo_should_exist != old_pseudo_should_exist {
1142 data.damage |= RestyleDamage::reconstruct();
1143 return child_restyle_hint;
1144 }
1145 },
1146 }
1147 }
1148
1149 child_restyle_hint
1150 }
1151
1152 fn replace_rules(
1157 &self,
1158 replacements: RestyleHint,
1159 context: &mut StyleContext<Self>,
1160 cascade_inputs: &mut ElementCascadeInputs,
1161 ) -> bool {
1162 let mut result = false;
1163 result |= self.replace_rules_internal(
1164 replacements,
1165 context,
1166 CascadeVisitedMode::Unvisited,
1167 cascade_inputs,
1168 );
1169 result |= self.replace_rules_internal(
1170 replacements,
1171 context,
1172 CascadeVisitedMode::Visited,
1173 cascade_inputs,
1174 );
1175 result
1176 }
1177
1178 fn compute_style_difference(
1182 &self,
1183 old_values: &ComputedValues,
1184 new_values: &ComputedValues,
1185 pseudo: Option<&PseudoElement>,
1186 ) -> StyleDifference {
1187 debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
1188 #[cfg(feature = "gecko")]
1189 {
1190 RestyleDamage::compute_style_difference(old_values, new_values)
1191 }
1192 #[cfg(feature = "servo")]
1193 {
1194 RestyleDamage::compute_style_difference::<Self>(old_values, new_values)
1195 }
1196 }
1197}
1198
1199impl<E: TElement> MatchMethods for E {}