summaryrefslogtreecommitdiffstats
path: root/servo/components/style/matching.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
commit8dd16259287f58f9273002717ec4d27e97127719 (patch)
tree3863e62a53829a84037444beab3abd4ed9dfc7d0 /servo/components/style/matching.rs
parentReleasing progress-linux version 126.0.1-1~progress7.99u1. (diff)
downloadfirefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz
firefox-8dd16259287f58f9273002717ec4d27e97127719.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/components/style/matching.rs')
-rw-r--r--servo/components/style/matching.rs220
1 files changed, 171 insertions, 49 deletions
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;
@@ -385,6 +387,158 @@ trait PrivateMatchMethods: TElement {
}
#[cfg(feature = "gecko")]
+ fn resolve_starting_style(
+ &self,
+ context: &mut StyleContext<Self>,
+ ) -> 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<Self>,
+ old_values: Option<&Arc<ComputedValues>>,
+ new_styles: &ResolvedElementStyles,
+ ) -> Option<Arc<ComputedValues>> {
+ // 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<Self>,
+ old_values: Option<&Arc<ComputedValues>>,
+ new_styles: &mut ResolvedElementStyles,
+ ) -> Option<Arc<ComputedValues>> {
+ 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,
context: &mut StyleContext<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);
}
}