Update CSS animations in a SequentialTask.

We create the SequentialTask only if:

* We have no old computed values and we have animation name style in the new
  computed values.
* Any animation properties is changed.
* display property is changed from 'none' and we have animation name style.
* display property is changed to 'none'.

In a subsequent patch we skip the SequentialTask if we have no running
animations and the display propery is changed to 'none'.
This commit is contained in:
Hiroyuki Ikezoe 2017-03-10 11:10:12 +09:00
parent fdb8c48094
commit 9ff99d4608
6 changed files with 84 additions and 8 deletions

View file

@ -446,6 +446,10 @@ impl<'le> TElement for ServoLayoutElement<'le> {
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool { fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
self.element.has_selector_flags(flags) self.element.has_selector_flags(flags)
} }
fn update_animations(&self, _pseudo: Option<&PseudoElement>) {
panic!("this should be only called on gecko");
}
} }
impl<'le> PartialEq for ServoLayoutElement<'le> { impl<'le> PartialEq for ServoLayoutElement<'le> {

View file

@ -15,6 +15,7 @@ use euclid::Size2D;
use matching::StyleSharingCandidateCache; use matching::StyleSharingCandidateCache;
use parking_lot::RwLock; use parking_lot::RwLock;
use properties::ComputedValues; use properties::ComputedValues;
use selector_parser::PseudoElement;
use selectors::matching::ElementSelectorFlags; use selectors::matching::ElementSelectorFlags;
use servo_config::opts; use servo_config::opts;
use std::collections::HashMap; use std::collections::HashMap;
@ -181,6 +182,10 @@ pub enum SequentialTask<E: TElement> {
/// Sets selector flags. This is used when we need to set flags on an /// Sets selector flags. This is used when we need to set flags on an
/// element that we don't have exclusive access to (i.e. the parent). /// element that we don't have exclusive access to (i.e. the parent).
SetSelectorFlags(SendElement<E>, ElementSelectorFlags), SetSelectorFlags(SendElement<E>, ElementSelectorFlags),
/// Marks that we need to create/remove/update CSS animations after the
/// first traversal.
UpdateAnimations(SendElement<E>, Option<PseudoElement>),
} }
impl<E: TElement> SequentialTask<E> { impl<E: TElement> SequentialTask<E> {
@ -192,6 +197,9 @@ impl<E: TElement> SequentialTask<E> {
SetSelectorFlags(el, flags) => { SetSelectorFlags(el, flags) => {
unsafe { el.set_selector_flags(flags) }; unsafe { el.set_selector_flags(flags) };
} }
UpdateAnimations(el, pseudo) => {
unsafe { el.update_animations(pseudo.as_ref()) };
}
} }
} }
@ -200,6 +208,12 @@ impl<E: TElement> SequentialTask<E> {
use self::SequentialTask::*; use self::SequentialTask::*;
SetSelectorFlags(unsafe { SendElement::new(el) }, flags) SetSelectorFlags(unsafe { SendElement::new(el) }, flags)
} }
/// Creates a task to update CSS Animations on a given (pseudo-)element.
pub fn update_animations(el: E, pseudo: Option<PseudoElement>) -> Self {
use self::SequentialTask::*;
UpdateAnimations(unsafe { SendElement::new(el) }, pseudo)
}
} }
/// A thread-local style context. /// A thread-local style context.

View file

@ -335,6 +335,10 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
/// Returns true if the element has all the specified selector flags. /// Returns true if the element has all the specified selector flags.
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool; fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
/// Creates a task to update CSS Animations on a given (pseudo-)element.
/// Note: Gecko only.
fn update_animations(&self, _pseudo: Option<&PseudoElement>);
} }
/// TNode and TElement aren't Send because we want to be careful and explicit /// TNode and TElement aren't Send because we want to be careful and explicit

View file

@ -33,12 +33,14 @@ use gecko_bindings::bindings::Gecko_GetAnimationRule;
use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
use gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock;
use gecko_bindings::bindings::Gecko_GetStyleContext; use gecko_bindings::bindings::Gecko_GetStyleContext;
use gecko_bindings::bindings::Gecko_UpdateAnimations;
use gecko_bindings::structs; use gecko_bindings::structs;
use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode}; use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode};
use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext}; use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext};
use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel; use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
use gecko_bindings::structs::NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO; use gecko_bindings::structs::NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
use gecko_bindings::sugar::ownership::HasArcFFI;
use parking_lot::RwLock; use parking_lot::RwLock;
use parser::ParserContextExtraData; use parser::ParserContextExtraData;
use properties::{ComputedValues, parse_style_attribute}; use properties::{ComputedValues, parse_style_attribute};
@ -504,6 +506,26 @@ impl<'le> TElement for GeckoElement<'le> {
let node_flags = selector_flags_to_node_flags(flags); let node_flags = selector_flags_to_node_flags(flags);
(self.flags() & node_flags) == node_flags (self.flags() & node_flags) == node_flags
} }
fn update_animations(&self, pseudo: Option<&PseudoElement>) {
let atom_ptr = PseudoElement::ns_atom_or_null_from_opt(pseudo);
let computed_data = self.borrow_data().unwrap();
let computed_values = computed_data.styles().primary.values();
let parent_element = self.parent_element();
let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
let parent_values = parent_data.as_ref().map(|d| d.styles().primary.values());
let parent_values_opt = parent_values.map(|v|
*HasArcFFI::arc_as_borrowed(v)
);
unsafe {
Gecko_UpdateAnimations(self.0, atom_ptr,
HasArcFFI::arc_as_borrowed(&computed_values),
parent_values_opt);
}
}
} }
impl<'le> PartialEq for GeckoElement<'le> { impl<'le> PartialEq for GeckoElement<'le> {

View file

@ -541,6 +541,14 @@ extern "C" {
RawGeckoStyleAnimationListBorrowed) RawGeckoStyleAnimationListBorrowed)
-> bool; -> bool;
} }
extern "C" {
pub fn Gecko_UpdateAnimations(aElement: RawGeckoElementBorrowed,
aPseudoTagOrNull: *mut nsIAtom,
aComputedValues:
ServoComputedValuesBorrowed,
aParentComputedValues:
ServoComputedValuesBorrowedOrNull);
}
extern "C" { extern "C" {
pub fn Gecko_Atomize(aString: *const ::std::os::raw::c_char, aLength: u32) pub fn Gecko_Atomize(aString: *const ::std::os::raw::c_char, aLength: u32)
-> *mut nsIAtom; -> *mut nsIAtom;

View file

@ -556,7 +556,7 @@ trait PrivateMatchMethods: TElement {
/// Computes values and damage for the primary or pseudo style of an element, /// Computes values and damage for the primary or pseudo style of an element,
/// setting them on the ElementData. /// setting them on the ElementData.
fn cascade_primary_or_pseudo<'a>(&self, fn cascade_primary_or_pseudo<'a>(&self,
context: &StyleContext<Self>, context: &mut StyleContext<Self>,
data: &mut ElementData, data: &mut ElementData,
pseudo: Option<&PseudoElement>, pseudo: Option<&PseudoElement>,
possibly_expired_animations: &mut Vec<PropertyAnimation>, possibly_expired_animations: &mut Vec<PropertyAnimation>,
@ -575,7 +575,7 @@ trait PrivateMatchMethods: TElement {
// Handle animations. // Handle animations.
if booleans.animate { if booleans.animate {
self.process_animations(&context, self.process_animations(context,
&mut old_values, &mut old_values,
&mut new_values, &mut new_values,
pseudo, pseudo,
@ -597,16 +597,40 @@ trait PrivateMatchMethods: TElement {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn process_animations(&self, fn process_animations(&self,
_context: &StyleContext<Self>, context: &mut StyleContext<Self>,
_old_values: &mut Option<Arc<ComputedValues>>, old_values: &mut Option<Arc<ComputedValues>>,
_new_values: &mut Arc<ComputedValues>, new_values: &mut Arc<ComputedValues>,
_pseudo: Option<&PseudoElement>, pseudo: Option<&PseudoElement>,
_possibly_expired_animations: &mut Vec<PropertyAnimation>) { _possibly_expired_animations: &mut Vec<PropertyAnimation>) {
let ref new_box_style = new_values.get_box();
let has_new_animation_style = new_box_style.animation_name_count() >= 1 &&
new_box_style.animation_name_at(0).0.len() != 0;
let needs_update_animations =
old_values.as_ref().map_or(has_new_animation_style, |ref old| {
let ref old_box_style = old.get_box();
let old_display_style = old_box_style.clone_display();
let new_display_style = new_box_style.clone_display();
// FIXME: If we know that the element has no running CSS animations,
// we can also skip the case with checking no_animations.
//
// FIXME: Bug 1344581: We still need to compare keyframe rules.
!old_box_style.animations_equals(&new_box_style) ||
(old_display_style == display::T::none &&
new_display_style != display::T::none &&
has_new_animation_style) ||
(old_display_style != display::T::none &&
new_display_style == display::T::none)
});
if needs_update_animations {
let task = SequentialTask::update_animations(self.as_node().as_element().unwrap(),
pseudo.cloned());
context.thread_local.tasks.push(task);
}
} }
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
fn process_animations(&self, fn process_animations(&self,
context: &StyleContext<Self>, context: &mut StyleContext<Self>,
old_values: &mut Option<Arc<ComputedValues>>, old_values: &mut Option<Arc<ComputedValues>>,
new_values: &mut Arc<ComputedValues>, new_values: &mut Arc<ComputedValues>,
_pseudo: Option<&PseudoElement>, _pseudo: Option<&PseudoElement>,
@ -1033,7 +1057,7 @@ pub trait MatchMethods : TElement {
/// Run the CSS cascade and compute values for the element, potentially /// Run the CSS cascade and compute values for the element, potentially
/// starting any new transitions or animations. /// starting any new transitions or animations.
fn cascade_element(&self, fn cascade_element(&self,
context: &StyleContext<Self>, context: &mut StyleContext<Self>,
mut data: &mut AtomicRefMut<ElementData>, mut data: &mut AtomicRefMut<ElementData>,
primary_is_shareable: bool) primary_is_shareable: bool)
{ {