mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Add animation and transition support for pseudo-elements
This change extends the DocumentAnimationSet to hold animations for pseudo-elements. Since pseudo-elements in Servo are not in the DOM like in Gecko, they need to be handled a bit carefully in stylo. When a pseudo-element has an animation, recascade the style. Finally, this change passes the pseudo-element string properly to animation events. Fixes: #10316
This commit is contained in:
parent
ba5568a0a6
commit
f3e373bc62
19 changed files with 359 additions and 138 deletions
|
@ -10,7 +10,7 @@
|
|||
use crate::computed_value_flags::ComputedValueFlags;
|
||||
use crate::context::{CascadeInputs, ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
|
||||
use crate::context::{SharedStyleContext, StyleContext};
|
||||
use crate::data::ElementData;
|
||||
use crate::data::{ElementData, ElementStyles};
|
||||
use crate::dom::TElement;
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::dom::TNode;
|
||||
|
@ -90,13 +90,13 @@ enum CascadeVisitedMode {
|
|||
|
||||
trait PrivateMatchMethods: TElement {
|
||||
fn replace_single_rule_node(
|
||||
context: &mut StyleContext<Self>,
|
||||
context: &SharedStyleContext,
|
||||
level: CascadeLevel,
|
||||
pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
|
||||
path: &mut StrongRuleNode,
|
||||
) -> bool {
|
||||
let stylist = &context.shared.stylist;
|
||||
let guards = &context.shared.guards;
|
||||
let stylist = &context.stylist;
|
||||
let guards = &context.guards;
|
||||
|
||||
let mut important_rules_changed = false;
|
||||
let new_node = stylist.rule_tree().update_rule_at_level(
|
||||
|
@ -143,13 +143,13 @@ trait PrivateMatchMethods: TElement {
|
|||
if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
|
||||
let style_attribute = self.style_attribute();
|
||||
result |= Self::replace_single_rule_node(
|
||||
context,
|
||||
context.shared,
|
||||
CascadeLevel::same_tree_author_normal(),
|
||||
style_attribute,
|
||||
primary_rules,
|
||||
);
|
||||
result |= Self::replace_single_rule_node(
|
||||
context,
|
||||
context.shared,
|
||||
CascadeLevel::same_tree_author_important(),
|
||||
style_attribute,
|
||||
primary_rules,
|
||||
|
@ -170,7 +170,7 @@ trait PrivateMatchMethods: TElement {
|
|||
|
||||
if replacements.contains(RestyleHint::RESTYLE_SMIL) {
|
||||
Self::replace_single_rule_node(
|
||||
context,
|
||||
context.shared,
|
||||
CascadeLevel::SMILOverride,
|
||||
self.smil_override(),
|
||||
primary_rules,
|
||||
|
@ -179,7 +179,7 @@ trait PrivateMatchMethods: TElement {
|
|||
|
||||
if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
|
||||
Self::replace_single_rule_node(
|
||||
context,
|
||||
context.shared,
|
||||
CascadeLevel::Transitions,
|
||||
self.transition_rule(&context.shared)
|
||||
.as_ref()
|
||||
|
@ -190,7 +190,7 @@ trait PrivateMatchMethods: TElement {
|
|||
|
||||
if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
|
||||
Self::replace_single_rule_node(
|
||||
context,
|
||||
context.shared,
|
||||
CascadeLevel::Animations,
|
||||
self.animation_rule(&context.shared)
|
||||
.as_ref()
|
||||
|
@ -245,11 +245,12 @@ trait PrivateMatchMethods: TElement {
|
|||
context: &mut StyleContext<Self>,
|
||||
old_style: Option<&ComputedValues>,
|
||||
new_style: &ComputedValues,
|
||||
pseudo_element: Option<PseudoElement>,
|
||||
) -> bool {
|
||||
let new_box_style = new_style.get_box();
|
||||
let new_style_specifies_animations = new_box_style.specifies_animations();
|
||||
|
||||
let has_animations = self.has_css_animations(&context.shared);
|
||||
let has_animations = self.has_css_animations(&context.shared, pseudo_element);
|
||||
if !new_style_specifies_animations && !has_animations {
|
||||
return false;
|
||||
}
|
||||
|
@ -326,6 +327,7 @@ trait PrivateMatchMethods: TElement {
|
|||
context: &StyleContext<Self>,
|
||||
old_style: Option<&ComputedValues>,
|
||||
new_style: &ComputedValues,
|
||||
pseudo_element: Option<PseudoElement>,
|
||||
) -> bool {
|
||||
let old_style = match old_style {
|
||||
Some(v) => v,
|
||||
|
@ -333,7 +335,9 @@ trait PrivateMatchMethods: TElement {
|
|||
};
|
||||
|
||||
let new_box_style = new_style.get_box();
|
||||
if !self.has_css_transitions(context.shared) && !new_box_style.specifies_transitions() {
|
||||
if !self.has_css_transitions(context.shared, pseudo_element) &&
|
||||
!new_box_style.specifies_transitions()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -379,7 +383,7 @@ trait PrivateMatchMethods: TElement {
|
|||
fn process_animations(
|
||||
&self,
|
||||
context: &mut StyleContext<Self>,
|
||||
old_values: &mut Option<Arc<ComputedValues>>,
|
||||
old_styles: &mut ElementStyles,
|
||||
new_styles: &mut ResolvedElementStyles,
|
||||
restyle_hint: RestyleHint,
|
||||
important_rules_changed: bool,
|
||||
|
@ -401,7 +405,12 @@ trait PrivateMatchMethods: TElement {
|
|||
// in addition to the unvisited styles.
|
||||
|
||||
let mut tasks = UpdateAnimationsTasks::empty();
|
||||
if self.needs_animations_update(context, old_values.as_ref().map(|s| &**s), new_values) {
|
||||
if self.needs_animations_update(
|
||||
context,
|
||||
old_values.as_ref().map(|s| &**s),
|
||||
new_values,
|
||||
/* pseudo_element = */ None,
|
||||
) {
|
||||
tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
|
||||
}
|
||||
|
||||
|
@ -409,6 +418,7 @@ trait PrivateMatchMethods: TElement {
|
|||
context,
|
||||
old_values.as_ref().map(|s| &**s),
|
||||
new_values,
|
||||
/* pseudo_element = */ None,
|
||||
) {
|
||||
let after_change_style = if self.has_css_transitions(context.shared) {
|
||||
self.after_change_style(context, new_values)
|
||||
|
@ -464,57 +474,149 @@ trait PrivateMatchMethods: TElement {
|
|||
fn process_animations(
|
||||
&self,
|
||||
context: &mut StyleContext<Self>,
|
||||
old_values: &mut Option<Arc<ComputedValues>>,
|
||||
old_styles: &mut ElementStyles,
|
||||
new_resolved_styles: &mut ResolvedElementStyles,
|
||||
_restyle_hint: RestyleHint,
|
||||
_important_rules_changed: bool,
|
||||
) {
|
||||
if !self.process_animations_for_style(
|
||||
use crate::animation::AnimationSetKey;
|
||||
use crate::dom::TDocument;
|
||||
|
||||
let style_changed = self.process_animations_for_style(
|
||||
context,
|
||||
old_values,
|
||||
&mut old_styles.primary,
|
||||
new_resolved_styles.primary_style_mut(),
|
||||
) {
|
||||
/* pseudo_element = */ None,
|
||||
);
|
||||
|
||||
// If we have modified animation or transitions, we recascade style for this node.
|
||||
if style_changed {
|
||||
let mut rule_node = new_resolved_styles.primary_style().rules().clone();
|
||||
let declarations = context.shared.animations.get_all_declarations(
|
||||
&AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()),
|
||||
context.shared.current_time_for_animations,
|
||||
self.as_node().owner_doc().shared_lock(),
|
||||
);
|
||||
Self::replace_single_rule_node(
|
||||
&context.shared,
|
||||
CascadeLevel::Transitions,
|
||||
declarations.transitions.as_ref().map(|a| a.borrow_arc()),
|
||||
&mut rule_node,
|
||||
);
|
||||
Self::replace_single_rule_node(
|
||||
&context.shared,
|
||||
CascadeLevel::Animations,
|
||||
declarations.animations.as_ref().map(|a| a.borrow_arc()),
|
||||
&mut rule_node,
|
||||
);
|
||||
|
||||
if rule_node != *new_resolved_styles.primary_style().rules() {
|
||||
let inputs = CascadeInputs {
|
||||
rules: Some(rule_node),
|
||||
visited_rules: new_resolved_styles.primary_style().visited_rules().cloned(),
|
||||
};
|
||||
|
||||
new_resolved_styles.primary.style = StyleResolverForElement::new(
|
||||
*self,
|
||||
context,
|
||||
RuleInclusion::All,
|
||||
PseudoElementResolution::IfApplicable,
|
||||
)
|
||||
.cascade_style_and_visited_with_default_parents(inputs);
|
||||
}
|
||||
}
|
||||
|
||||
self.process_animations_for_pseudo(
|
||||
context,
|
||||
old_styles,
|
||||
new_resolved_styles,
|
||||
PseudoElement::Before,
|
||||
);
|
||||
self.process_animations_for_pseudo(
|
||||
context,
|
||||
old_styles,
|
||||
new_resolved_styles,
|
||||
PseudoElement::After,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
fn process_animations_for_pseudo(
|
||||
&self,
|
||||
context: &mut StyleContext<Self>,
|
||||
old_styles: &mut ElementStyles,
|
||||
new_resolved_styles: &mut ResolvedElementStyles,
|
||||
pseudo_element: PseudoElement,
|
||||
) {
|
||||
use crate::animation::AnimationSetKey;
|
||||
use crate::dom::TDocument;
|
||||
|
||||
let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone());
|
||||
let mut style = match new_resolved_styles.pseudos.get(&pseudo_element) {
|
||||
Some(style) => Arc::clone(style),
|
||||
None => {
|
||||
context
|
||||
.shared
|
||||
.animations
|
||||
.cancel_all_animations_for_key(&key);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let mut old_style = old_styles.pseudos.get(&pseudo_element).cloned();
|
||||
self.process_animations_for_style(
|
||||
context,
|
||||
&mut old_style,
|
||||
&mut style,
|
||||
Some(pseudo_element.clone()),
|
||||
);
|
||||
|
||||
let declarations = context.shared.animations.get_all_declarations(
|
||||
&key,
|
||||
context.shared.current_time_for_animations,
|
||||
self.as_node().owner_doc().shared_lock(),
|
||||
);
|
||||
if declarations.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have modified animation or transitions, we recascade style for this node.
|
||||
let mut rule_node = new_resolved_styles.primary_style().rules().clone();
|
||||
let mut rule_node = style.rules().clone();
|
||||
Self::replace_single_rule_node(
|
||||
context,
|
||||
&context.shared,
|
||||
CascadeLevel::Transitions,
|
||||
self.transition_rule(&context.shared)
|
||||
.as_ref()
|
||||
.map(|a| a.borrow_arc()),
|
||||
declarations.transitions.as_ref().map(|a| a.borrow_arc()),
|
||||
&mut rule_node,
|
||||
);
|
||||
Self::replace_single_rule_node(
|
||||
context,
|
||||
&context.shared,
|
||||
CascadeLevel::Animations,
|
||||
self.animation_rule(&context.shared)
|
||||
.as_ref()
|
||||
.map(|a| a.borrow_arc()),
|
||||
declarations.animations.as_ref().map(|a| a.borrow_arc()),
|
||||
&mut rule_node,
|
||||
);
|
||||
|
||||
// If these animations haven't modified the rule now, we can just exit early.
|
||||
if rule_node == *new_resolved_styles.primary_style().rules() {
|
||||
if rule_node == *style.rules() {
|
||||
return;
|
||||
}
|
||||
|
||||
let inputs = CascadeInputs {
|
||||
rules: Some(rule_node),
|
||||
visited_rules: new_resolved_styles.primary_style().visited_rules().cloned(),
|
||||
visited_rules: style.visited_rules().cloned(),
|
||||
};
|
||||
|
||||
let style = StyleResolverForElement::new(
|
||||
let new_style = StyleResolverForElement::new(
|
||||
*self,
|
||||
context,
|
||||
RuleInclusion::All,
|
||||
PseudoElementResolution::IfApplicable,
|
||||
)
|
||||
.cascade_style_and_visited_with_default_parents(inputs);
|
||||
.cascade_style_and_visited_for_pseudo_with_default_parents(
|
||||
inputs,
|
||||
&pseudo_element,
|
||||
&new_resolved_styles.primary,
|
||||
);
|
||||
|
||||
new_resolved_styles.primary.style = style;
|
||||
new_resolved_styles
|
||||
.pseudos
|
||||
.set(&pseudo_element, new_style.0);
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
|
@ -523,17 +625,24 @@ trait PrivateMatchMethods: TElement {
|
|||
context: &mut StyleContext<Self>,
|
||||
old_values: &mut Option<Arc<ComputedValues>>,
|
||||
new_values: &mut Arc<ComputedValues>,
|
||||
pseudo_element: Option<PseudoElement>,
|
||||
) -> bool {
|
||||
use crate::animation::{AnimationSetKey, AnimationState};
|
||||
|
||||
// We need to call this before accessing the `ElementAnimationSet` from the
|
||||
// map because this call will do a RwLock::read().
|
||||
let needs_animations_update =
|
||||
self.needs_animations_update(context, old_values.as_ref().map(|s| &**s), new_values);
|
||||
let needs_animations_update = self.needs_animations_update(
|
||||
context,
|
||||
old_values.as_ref().map(|s| &**s),
|
||||
new_values,
|
||||
pseudo_element,
|
||||
);
|
||||
|
||||
let might_need_transitions_update = self.might_need_transitions_update(
|
||||
context,
|
||||
old_values.as_ref().map(|s| &**s),
|
||||
new_values,
|
||||
pseudo_element,
|
||||
);
|
||||
|
||||
let mut after_change_style = None;
|
||||
|
@ -541,7 +650,7 @@ trait PrivateMatchMethods: TElement {
|
|||
after_change_style = self.after_change_style(context, new_values);
|
||||
}
|
||||
|
||||
let key = AnimationSetKey(self.as_node().opaque());
|
||||
let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
|
||||
let shared_context = context.shared;
|
||||
let mut animation_set = shared_context
|
||||
.animations
|
||||
|
@ -761,7 +870,7 @@ pub trait MatchMethods: TElement {
|
|||
|
||||
self.process_animations(
|
||||
context,
|
||||
&mut data.styles.primary,
|
||||
&mut data.styles,
|
||||
&mut new_styles,
|
||||
data.hint,
|
||||
important_rules_changed,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue