From a90a5cba08fdf6c0ceb95101c275108a152a3aed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:35:37 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- servo/components/style/matching.rs | 220 ++++++++++++++++++++++++++++--------- 1 file changed, 171 insertions(+), 49 deletions(-) (limited to 'servo/components/style/matching.rs') diff --git a/servo/components/style/matching.rs b/servo/components/style/matching.rs index e9d754aa10..739504d260 100644 --- a/servo/components/style/matching.rs +++ b/servo/components/style/matching.rs @@ -21,8 +21,10 @@ use crate::properties::PropertyDeclarationBlock; use crate::rule_tree::{CascadeLevel, StrongRuleNode}; use crate::selector_parser::{PseudoElement, RestyleDamage}; use crate::shared_lock::Locked; -use crate::style_resolver::ResolvedElementStyles; -use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement}; +use crate::style_resolver::{PseudoElementResolution, ResolvedElementStyles}; +#[cfg(feature = "gecko")] +use crate::style_resolver::ResolvedStyle; +use crate::style_resolver::StyleResolverForElement; use crate::stylesheets::layer_rule::LayerOrder; use crate::stylist::RuleInclusion; use crate::traversal_flags::TraversalFlags; @@ -384,6 +386,158 @@ trait PrivateMatchMethods: TElement { } } + #[cfg(feature = "gecko")] + fn resolve_starting_style( + &self, + context: &mut StyleContext, + ) -> ResolvedStyle { + use selectors::matching::IncludeStartingStyle; + + // Compute after-change style for the parent and the layout parent. + // Per spec, starting style inherits from the parent’s after-change style just like + // after-change style does. + let parent_el = self.inheritance_parent(); + let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data()); + let parent_style = parent_data.as_ref().map(|d| d.styles.primary()); + let parent_after_change_style = + parent_style.and_then(|s| self.after_change_style(context, s)); + let parent_values = parent_after_change_style + .as_ref() + .or(parent_style) + .map(|x| &**x); + + let mut layout_parent_el = parent_el.clone(); + let layout_parent_data; + let layout_parent_after_change_style; + let layout_parent_values = if parent_style.map_or(false, |s| s.is_display_contents()) { + layout_parent_el = Some(layout_parent_el.unwrap().layout_parent()); + layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap(); + let layout_parent_style = Some(layout_parent_data.styles.primary()); + layout_parent_after_change_style = + layout_parent_style.and_then(|s| self.after_change_style(context, s)); + layout_parent_after_change_style + .as_ref() + .or(layout_parent_style) + .map(|x| &**x) + } else { + parent_values + }; + + // Note: Basically, we have to remove transition rules because the starting style for an + // element is the after-change style with @starting-style rules applied in addition. + // However, we expect there is no transition rules for this element when calling this + // function because we do this only when we don't have before-change style or we change + // from display:none. In these cases, it's unlikely to have running transitions on this + // element. + let mut resolver = StyleResolverForElement::new( + *self, + context, + RuleInclusion::All, + PseudoElementResolution::IfApplicable, + ); + resolver + .resolve_primary_style( + parent_values, + layout_parent_values, + IncludeStartingStyle::Yes, + ) + .style + } + + #[cfg(feature = "gecko")] + fn maybe_resolve_starting_style( + &self, + context: &mut StyleContext, + old_values: Option<&Arc>, + new_styles: &ResolvedElementStyles, + ) -> Option> { + // For both cases: + // 1. If we didn't see any starting-style rules for this given element during full matching. + // 2. If there is no transitions specified. + // We don't have to resolve starting style. + if !new_styles.may_have_starting_style() + || !new_styles.primary_style().get_ui().specifies_transitions() + { + return None; + } + + // We resolve starting style only if we don't have before-change-style, or we change from + // display:none. + if old_values.is_some() + && !new_styles + .primary_style() + .is_display_property_changed_from_none(old_values.map(|s| &**s)) + { + return None; + } + + let starting_style = self.resolve_starting_style(context); + if starting_style.style().clone_display().is_none() { + return None; + } + + Some(starting_style.0) + } + + /// Handle CSS Transitions. Returns None if we don't need to update transitions. And it returns + /// the before-change style per CSS Transitions spec. + /// + /// Note: The before-change style could be the computed values of all properties on the element + /// as of the previous style change event, or the starting style if we don't have the valid + /// before-change style there. + #[cfg(feature = "gecko")] + fn process_transitions( + &self, + context: &mut StyleContext, + old_values: Option<&Arc>, + new_styles: &mut ResolvedElementStyles, + ) -> Option> { + let starting_values = self.maybe_resolve_starting_style(context, old_values, new_styles); + let before_change_or_starting = if starting_values.is_some() { + starting_values.as_ref() + } else { + old_values + }; + let new_values = new_styles.primary_style_mut(); + + if !self.might_need_transitions_update( + context, + before_change_or_starting.map(|s| &**s), + new_values, + /* pseudo_element = */ None, + ) { + return None; + } + + let after_change_style = + if self.has_css_transitions(context.shared, /* pseudo_element = */ None) { + self.after_change_style(context, new_values) + } else { + None + }; + + // In order to avoid creating a SequentialTask for transitions which + // may not be updated, we check it per property to make sure Gecko + // side will really update transition. + if !self.needs_transitions_update( + before_change_or_starting.unwrap(), + after_change_style.as_ref().unwrap_or(&new_values), + ) { + return None; + } + + if let Some(values_without_transitions) = after_change_style { + *new_values = values_without_transitions; + } + + // Move the new-created starting style, or clone the old values. + if starting_values.is_some() { + starting_values + } else { + old_values.cloned() + } + } + #[cfg(feature = "gecko")] fn process_animations( &self, @@ -395,13 +549,12 @@ trait PrivateMatchMethods: TElement { ) { use crate::context::UpdateAnimationsTasks; - let new_values = new_styles.primary_style_mut(); let old_values = &old_styles.primary; if context.shared.traversal_flags.for_animation_only() { self.handle_display_change_for_smil_if_needed( context, old_values.as_deref(), - new_values, + new_styles.primary_style(), restyle_hint, ); return; @@ -413,15 +566,15 @@ trait PrivateMatchMethods: TElement { let mut tasks = UpdateAnimationsTasks::empty(); if old_values.as_deref().map_or_else( - || new_values.get_ui().specifies_scroll_timelines(), - |old| !old.get_ui().scroll_timelines_equals(new_values.get_ui()), + || new_styles.primary_style().get_ui().specifies_scroll_timelines(), + |old| !old.get_ui().scroll_timelines_equals(new_styles.primary_style().get_ui()), ) { tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES); } if old_values.as_deref().map_or_else( - || new_values.get_ui().specifies_view_timelines(), - |old| !old.get_ui().view_timelines_equals(new_values.get_ui()), + || new_styles.primary_style().get_ui().specifies_view_timelines(), + |old| !old.get_ui().view_timelines_equals(new_styles.primary_style().get_ui()), ) { tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES); } @@ -429,58 +582,27 @@ trait PrivateMatchMethods: TElement { if self.needs_animations_update( context, old_values.as_deref(), - new_values, + new_styles.primary_style(), /* pseudo_element = */ None, ) { tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS); } - let before_change_style = if self.might_need_transitions_update( - context, - old_values.as_deref(), - new_values, - /* pseudo_element = */ None, - ) { - let after_change_style = - if self.has_css_transitions(context.shared, /* pseudo_element = */ None) { - self.after_change_style(context, new_values) - } else { - None - }; - - // In order to avoid creating a SequentialTask for transitions which - // may not be updated, we check it per property to make sure Gecko - // side will really update transition. - let needs_transitions_update = { - // We borrow new_values here, so need to add a scope to make - // sure we release it before assigning a new value to it. - let after_change_style_ref = after_change_style.as_ref().unwrap_or(&new_values); - - self.needs_transitions_update(old_values.as_ref().unwrap(), after_change_style_ref) - }; - - if needs_transitions_update { - if let Some(values_without_transitions) = after_change_style { - *new_values = values_without_transitions; - } - tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS); - - // We need to clone old_values into SequentialTask, so we can - // use it later. - old_values.clone() - } else { - None - } - } else { - None - }; + let before_change_style = + self.process_transitions(context, old_values.as_ref(), new_styles); + if before_change_style.is_some() { + tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS); + } if self.has_animations(&context.shared) { tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES); if important_rules_changed { tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS); } - if new_values.is_display_property_changed_from_none(old_values.as_deref()) { + if new_styles + .primary_style() + .is_display_property_changed_from_none(old_values.as_deref()) + { tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE); } } -- cgit v1.2.3