aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2020-05-07 18:37:18 +0200
committerMartin Robinson <mrobinson@igalia.com>2020-05-12 10:22:14 +0200
commit3b0619aedd967238091367f6e03dabf262e116ad (patch)
treeb2427a9f19befc3017860053d3a416fda3d87275
parentaa9f16ce45f6eb22ffd5bc6a10802ded4cc2319b (diff)
downloadservo-3b0619aedd967238091367f6e03dabf262e116ad.tar.gz
servo-3b0619aedd967238091367f6e03dabf262e116ad.zip
Move most animation processing to script
This is preparation for sharing this code with layout_2020 and implementing selective off-the-main-thread animations. We still look for nodes not in the flow tree in the layout thread.
-rw-r--r--Cargo.lock3
-rw-r--r--components/constellation/constellation.rs3
-rw-r--r--components/layout/animation.rs218
-rw-r--r--components/layout/context.rs4
-rw-r--r--components/layout/lib.rs1
-rw-r--r--components/layout_thread/lib.rs75
-rw-r--r--components/layout_thread_2020/lib.rs4
-rw-r--r--components/msg/constellation_msg.rs1
-rw-r--r--components/script/Cargo.toml1
-rw-r--r--components/script/animation_timeline.rs2
-rw-r--r--components/script/animations.rs293
-rw-r--r--components/script/dom/bindings/trace.rs32
-rw-r--r--components/script/dom/document.rs20
-rw-r--r--components/script/dom/window.rs12
-rw-r--r--components/script/lib.rs1
-rw-r--r--components/script/script_thread.rs56
-rw-r--r--components/script_layout_interface/Cargo.toml2
-rw-r--r--components/script_layout_interface/message.rs16
-rw-r--r--components/script_traits/lib.rs55
-rw-r--r--components/script_traits/script_msg.rs3
-rw-r--r--components/style/animation.rs13
21 files changed, 444 insertions, 371 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fab3ceb8e28..0f5502f8c31 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4562,6 +4562,7 @@ dependencies = [
"enum-iterator",
"euclid",
"fnv",
+ "fxhash",
"headers",
"html5ever",
"http",
@@ -4641,6 +4642,7 @@ dependencies = [
"canvas_traits",
"crossbeam-channel",
"euclid",
+ "fxhash",
"gfx_traits",
"html5ever",
"ipc-channel",
@@ -4650,6 +4652,7 @@ dependencies = [
"metrics",
"msg",
"net_traits",
+ "parking_lot",
"profile_traits",
"range",
"script_traits",
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 00dcd2a4eb8..423314e6b0a 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -2237,9 +2237,6 @@ where
fn handle_request_from_layout(&mut self, message: FromLayoutMsg) {
debug!("Constellation got {:?} message", message);
match message {
- FromLayoutMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => {
- self.handle_change_running_animations_state(pipeline_id, animation_state)
- },
// Layout sends new sizes for all subframes. This needs to be reflected by all
// frame trees in the navigation context containing the subframe.
FromLayoutMsg::IFrameSizes(iframe_sizes) => {
diff --git a/components/layout/animation.rs b/components/layout/animation.rs
deleted file mode 100644
index a331b3b3bbc..00000000000
--- a/components/layout/animation.rs
+++ /dev/null
@@ -1,218 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS transitions and animations.
-
-use crate::display_list::items::OpaqueNode;
-use crate::flow::{Flow, GetBaseFlow};
-use crate::opaque_node::OpaqueNodeMethods;
-use fxhash::{FxHashMap, FxHashSet};
-use ipc_channel::ipc::IpcSender;
-use msg::constellation_msg::PipelineId;
-use script_traits::UntrustedNodeAddress;
-use script_traits::{
- AnimationState as AnimationsPresentState, ConstellationControlMsg,
- LayoutMsg as ConstellationMsg, TransitionOrAnimationEvent, TransitionOrAnimationEventType,
-};
-use style::animation::{AnimationState, ElementAnimationSet};
-
-/// Processes any new animations that were discovered after style recalculation and
-/// remove animations for any disconnected nodes. Send messages that trigger events
-/// for any events that changed state.
-pub fn do_post_style_animations_update(
- constellation_chan: &IpcSender<ConstellationMsg>,
- script_chan: &IpcSender<ConstellationControlMsg>,
- animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationSet>,
- pipeline_id: PipelineId,
- now: f64,
- out: &mut Vec<UntrustedNodeAddress>,
- root_flow: &mut dyn Flow,
-) {
- let had_running_animations = animation_states
- .values()
- .any(|state| state.needs_animation_ticks());
-
- cancel_animations_for_disconnected_nodes(animation_states, root_flow);
- collect_newly_animating_nodes(animation_states, out);
-
- for (node, animation_state) in animation_states.iter_mut() {
- update_animation_state(script_chan, animation_state, pipeline_id, now, *node);
- }
-
- // Remove empty states from our collection of states in order to free
- // up space as soon as we are no longer tracking any animations for
- // a node.
- animation_states.retain(|_, state| !state.is_empty());
-
- let have_running_animations = animation_states
- .values()
- .any(|state| state.needs_animation_ticks());
- let present = match (had_running_animations, have_running_animations) {
- (true, false) => AnimationsPresentState::NoAnimationsPresent,
- (false, true) => AnimationsPresentState::AnimationsPresent,
- _ => return,
- };
- constellation_chan
- .send(ConstellationMsg::ChangeRunningAnimationsState(
- pipeline_id,
- present,
- ))
- .unwrap();
-}
-
-/// Collect newly animating nodes, which is used by the script process during
-/// forced, synchronous reflows to root DOM nodes for the duration of their
-/// animations or transitions.
-pub fn collect_newly_animating_nodes(
- animation_states: &FxHashMap<OpaqueNode, ElementAnimationSet>,
- out: &mut Vec<UntrustedNodeAddress>,
-) {
- // This extends the output vector with an iterator that contains a copy of the node
- // address for every new animation. The script thread currently stores a rooted node
- // for every property that is transitioning. The current strategy of repeating the
- // node address is a holdover from when the code here looked different.
- out.extend(animation_states.iter().flat_map(|(node, state)| {
- let mut num_new_animations = state
- .animations
- .iter()
- .filter(|animation| animation.is_new)
- .count();
- num_new_animations += state
- .transitions
- .iter()
- .filter(|transition| transition.is_new)
- .count();
- std::iter::repeat(node.to_untrusted_node_address()).take(num_new_animations)
- }));
-}
-
-/// Cancel animations for any nodes which have been removed from the DOM or are display:none.
-/// We detect this by looking for nodes that are used in the flow tree.
-/// TODO(mrobinson): We should look into a way of doing this during flow tree construction.
-/// This also doesn't yet handles nodes that have been reparented.
-pub fn cancel_animations_for_disconnected_nodes(
- animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationSet>,
- root_flow: &mut dyn Flow,
-) {
- // Assume all nodes have been removed until proven otherwise.
- let mut invalid_nodes: FxHashSet<OpaqueNode> = animation_states.keys().cloned().collect();
- fn traverse_flow(flow: &mut dyn Flow, invalid_nodes: &mut FxHashSet<OpaqueNode>) {
- flow.mutate_fragments(&mut |fragment| {
- invalid_nodes.remove(&fragment.node);
- });
- for kid in flow.mut_base().children.iter_mut() {
- traverse_flow(kid, invalid_nodes)
- }
- }
- traverse_flow(root_flow, &mut invalid_nodes);
-
- // Cancel animations for any nodes that are no longer in the flow tree.
- for node in &invalid_nodes {
- if let Some(state) = animation_states.get_mut(node) {
- state.cancel_all_animations();
- }
- }
-}
-
-fn update_animation_state(
- script_channel: &IpcSender<ConstellationControlMsg>,
- animation_state: &mut ElementAnimationSet,
- pipeline_id: PipelineId,
- now: f64,
- node: OpaqueNode,
-) {
- let send_event = |property_or_animation_name, event_type, elapsed_time| {
- script_channel
- .send(ConstellationControlMsg::TransitionOrAnimationEvent(
- TransitionOrAnimationEvent {
- pipeline_id,
- event_type,
- node: node.to_untrusted_node_address(),
- property_or_animation_name,
- elapsed_time,
- },
- ))
- .unwrap()
- };
-
- handle_canceled_animations(animation_state, now, send_event);
- finish_running_animations(animation_state, now, send_event);
- handle_new_animations(animation_state, send_event);
-}
-
-/// Walk through the list of running animations and remove all of the ones that
-/// have ended.
-fn finish_running_animations(
- animation_state: &mut ElementAnimationSet,
- now: f64,
- mut send_event: impl FnMut(String, TransitionOrAnimationEventType, f64),
-) {
- for animation in animation_state.animations.iter_mut() {
- if animation.state == AnimationState::Running && animation.has_ended(now) {
- animation.state = AnimationState::Finished;
- send_event(
- animation.name.to_string(),
- TransitionOrAnimationEventType::AnimationEnd,
- animation.active_duration(),
- );
- }
- }
-
- for transition in animation_state.transitions.iter_mut() {
- if transition.state == AnimationState::Running && transition.has_ended(now) {
- transition.state = AnimationState::Finished;
- send_event(
- transition.property_animation.property_name().into(),
- TransitionOrAnimationEventType::TransitionEnd,
- transition.property_animation.duration,
- );
- }
- }
-}
-
-/// Send events for canceled animations. Currently this only handles canceled
-/// transitions, but eventually this should handle canceled CSS animations as
-/// well.
-fn handle_canceled_animations(
- animation_state: &mut ElementAnimationSet,
- now: f64,
- mut send_event: impl FnMut(String, TransitionOrAnimationEventType, f64),
-) {
- for transition in &animation_state.transitions {
- if transition.state == AnimationState::Canceled {
- // TODO(mrobinson): We need to properly compute the elapsed_time here
- // according to https://drafts.csswg.org/css-transitions/#event-transitionevent
- send_event(
- transition.property_animation.property_name().into(),
- TransitionOrAnimationEventType::TransitionCancel,
- (now - transition.start_time).max(0.),
- );
- }
- }
-
- // TODO(mrobinson): We need to send animationcancel events.
- animation_state.clear_canceled_animations();
-}
-
-fn handle_new_animations(
- animation_state: &mut ElementAnimationSet,
- mut send_event: impl FnMut(String, TransitionOrAnimationEventType, f64),
-) {
- for animation in animation_state.animations.iter_mut() {
- animation.is_new = false;
- }
-
- for transition in animation_state.transitions.iter_mut() {
- if transition.is_new {
- // TODO(mrobinson): We need to properly compute the elapsed_time here
- // according to https://drafts.csswg.org/css-transitions/#event-transitionevent
- send_event(
- transition.property_animation.property_name().into(),
- TransitionOrAnimationEventType::TransitionRun,
- 0.,
- );
- transition.is_new = false;
- }
- }
-}
diff --git a/components/layout/context.rs b/components/layout/context.rs
index 00e0c5a0cc1..a17bc1db4ee 100644
--- a/components/layout/context.rs
+++ b/components/layout/context.rs
@@ -17,7 +17,6 @@ use net_traits::image_cache::{
use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState};
use script_traits::Painter;
-use script_traits::UntrustedNodeAddress;
use servo_atoms::Atom;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::cell::{RefCell, RefMut};
@@ -85,9 +84,6 @@ pub struct LayoutContext<'a> {
/// A list of in-progress image loads to be shared with the script thread.
pub pending_images: Mutex<Vec<PendingImage>>,
-
- /// A list of nodes that have just initiated a CSS transition or animation.
- pub newly_animating_nodes: Mutex<Vec<UntrustedNodeAddress>>,
}
impl<'a> Drop for LayoutContext<'a> {
diff --git a/components/layout/lib.rs b/components/layout/lib.rs
index fdf3971fc3f..98e04f7d93e 100644
--- a/components/layout/lib.rs
+++ b/components/layout/lib.rs
@@ -20,7 +20,6 @@ extern crate serde;
#[macro_use]
pub mod layout_debug;
-pub mod animation;
mod block;
pub mod construct;
pub mod context;
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index ff0b11875f7..037fffc34ec 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -29,7 +29,7 @@ use crossbeam_channel::{Receiver, Sender};
use embedder_traits::resources::{self, Resource};
use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D};
use fnv::FnvHashMap;
-use fxhash::FxHashMap;
+use fxhash::{FxHashMap, FxHashSet};
use gfx::font;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context;
@@ -37,7 +37,6 @@ use gfx_traits::{node_id_from_scroll_id, Epoch};
use histogram::Histogram;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
-use layout::animation;
use layout::construct::ConstructionResult;
use layout::context::malloc_size_of_persistent_local_context;
use layout::context::LayoutContext;
@@ -191,9 +190,6 @@ pub struct LayoutThread {
/// The document-specific shared lock used for author-origin stylesheets
document_shared_lock: Option<SharedRwLock>,
- /// The animation state for all of our nodes.
- animation_states: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>,
-
/// A counter for epoch messages
epoch: Cell<Epoch>,
@@ -552,7 +548,6 @@ impl LayoutThread {
outstanding_web_fonts: Arc::new(AtomicUsize::new(0)),
root_flow: RefCell::new(None),
document_shared_lock: None,
- animation_states: ServoArc::new(RwLock::new(Default::default())),
// Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR
epoch: Cell::new(Epoch(1)),
viewport_size: Size2D::new(Au(0), Au(0)),
@@ -613,6 +608,7 @@ impl LayoutThread {
snapshot_map: &'a SnapshotMap,
origin: ImmutableOrigin,
animation_timeline_value: f64,
+ animation_states: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>,
) -> LayoutContext<'a> {
LayoutContext {
id: self.id,
@@ -622,7 +618,7 @@ impl LayoutThread {
options: GLOBAL_STYLE_DATA.options.clone(),
guards,
visited_styles_enabled: false,
- animation_states: self.animation_states.clone(),
+ animation_states,
registered_speculative_painters: &self.registered_painters,
current_time_for_animations: animation_timeline_value,
traversal_flags: TraversalFlags::empty(),
@@ -632,7 +628,6 @@ impl LayoutThread {
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: Mutex::new(vec![]),
- newly_animating_nodes: Mutex::new(vec![]),
registered_painters: &self.registered_painters,
}
}
@@ -657,7 +652,6 @@ impl LayoutThread {
},
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
- Msg::GetRunningAnimations(..) => LayoutHangAnnotation::GetRunningAnimations,
};
self.background_hang_monitor
.as_ref()
@@ -824,15 +818,6 @@ impl LayoutThread {
Msg::SetNavigationStart(time) => {
self.paint_time_metrics.set_navigation_start(time);
},
- Msg::GetRunningAnimations(sender) => {
- let running_animation_count = self
- .animation_states
- .read()
- .values()
- .map(|state| state.running_animation_and_transition_count())
- .sum();
- let _ = sender.send(running_animation_count);
- },
}
true
@@ -1413,8 +1398,13 @@ impl LayoutThread {
self.stylist.flush(&guards, Some(element), Some(&map));
// Create a layout context for use throughout the following passes.
- let mut layout_context =
- self.build_layout_context(guards.clone(), &map, origin, data.animation_timeline_value);
+ let mut layout_context = self.build_layout_context(
+ guards.clone(),
+ &map,
+ origin,
+ data.animation_timeline_value,
+ data.animations.clone(),
+ );
let pool;
let (thread_pool, num_threads) = if self.parallel_flag {
@@ -1516,8 +1506,6 @@ impl LayoutThread {
) {
reflow_result.pending_images =
std::mem::replace(&mut *context.pending_images.lock().unwrap(), vec![]);
- reflow_result.newly_animating_nodes =
- std::mem::replace(&mut *context.newly_animating_nodes.lock().unwrap(), vec![]);
let mut root_flow = match self.root_flow.borrow().clone() {
Some(root_flow) => root_flow,
@@ -1629,6 +1617,33 @@ impl LayoutThread {
rw_data.scroll_offsets = layout_scroll_states
}
+ /// Cancel animations for any nodes which have been removed from flow tree.
+ /// TODO(mrobinson): We should look into a way of doing this during flow tree construction.
+ /// This also doesn't yet handles nodes that have been reparented.
+ fn cancel_animations_for_nodes_not_in_flow_tree(
+ animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationSet>,
+ root_flow: &mut dyn Flow,
+ ) {
+ // Assume all nodes have been removed until proven otherwise.
+ let mut invalid_nodes: FxHashSet<OpaqueNode> = animation_states.keys().cloned().collect();
+ fn traverse_flow(flow: &mut dyn Flow, invalid_nodes: &mut FxHashSet<OpaqueNode>) {
+ flow.mutate_fragments(&mut |fragment| {
+ invalid_nodes.remove(&fragment.node);
+ });
+ for kid in flow.mut_base().children.iter_mut() {
+ traverse_flow(kid, invalid_nodes)
+ }
+ }
+ traverse_flow(root_flow, &mut invalid_nodes);
+
+ // Cancel animations for any nodes that are no longer in the flow tree.
+ for node in &invalid_nodes {
+ if let Some(state) = animation_states.get_mut(node) {
+ state.cancel_all_animations();
+ }
+ }
+ }
+
fn perform_post_style_recalc_layout_passes(
&self,
root_flow: &mut FlowRef,
@@ -1638,18 +1653,10 @@ impl LayoutThread {
rw_data: &mut LayoutThreadData,
context: &mut LayoutContext,
) {
- {
- let mut newly_animating_nodes = context.newly_animating_nodes.lock().unwrap();
- animation::do_post_style_animations_update(
- &self.constellation_chan,
- &self.script_chan,
- &mut *(self.animation_states.write()),
- self.id,
- context.style_context.current_time_for_animations,
- &mut newly_animating_nodes,
- FlowRef::deref_mut(root_flow),
- );
- }
+ Self::cancel_animations_for_nodes_not_in_flow_tree(
+ &mut *(context.style_context.animation_states.write()),
+ FlowRef::deref_mut(root_flow),
+ );
profile(
profile_time::ProfilerCategory::LayoutRestyleDamagePropagation,
diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs
index 42a5a4d3e00..d97c29459ac 100644
--- a/components/layout_thread_2020/lib.rs
+++ b/components/layout_thread_2020/lib.rs
@@ -615,7 +615,6 @@ impl LayoutThread {
},
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
- Msg::GetRunningAnimations(..) => LayoutHangAnnotation::GetRunningAnimations,
};
self.background_hang_monitor
.as_ref()
@@ -766,9 +765,6 @@ impl LayoutThread {
Msg::SetNavigationStart(time) => {
self.paint_time_metrics.set_navigation_start(time);
},
- Msg::GetRunningAnimations(sender) => {
- let _ = sender.send(0);
- },
}
true
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index c70021421d6..8a599405df8 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -541,7 +541,6 @@ pub enum LayoutHangAnnotation {
UpdateScrollStateFromScript,
RegisterPaint,
SetNavigationStart,
- GetRunningAnimations,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index f4d8808c869..9f98ff30909 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -53,6 +53,7 @@ encoding_rs = "0.8"
enum-iterator = "0.3"
euclid = "0.20"
fnv = "1.0"
+fxhash = "0.2"
headers = "0.2"
html5ever = "0.25"
http = "0.1"
diff --git a/components/script/animation_timeline.rs b/components/script/animation_timeline.rs
index e0ad520db61..54127b94e9e 100644
--- a/components/script/animation_timeline.rs
+++ b/components/script/animation_timeline.rs
@@ -12,7 +12,7 @@ use time;
/// A `AnimationTimeline` which is used to synchronize animations during the script
/// event loop.
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
-pub struct AnimationTimeline {
+pub(crate) struct AnimationTimeline {
current_value: f64,
}
diff --git a/components/script/animations.rs b/components/script/animations.rs
new file mode 100644
index 00000000000..a666ac3ed84
--- /dev/null
+++ b/components/script/animations.rs
@@ -0,0 +1,293 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#![deny(missing_docs)]
+
+//! The set of animations for a document.
+
+use crate::dom::window::Window;
+use fxhash::FxHashMap;
+use libc::c_void;
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use msg::constellation_msg::PipelineId;
+use parking_lot::RwLock;
+use script_traits::{AnimationState as AnimationsPresentState, ScriptMsg, UntrustedNodeAddress};
+use servo_arc::Arc;
+use style::animation::{AnimationState, ElementAnimationSet};
+use style::dom::OpaqueNode;
+
+/// The set of animations for a document.
+///
+/// Make sure to update the MallocSizeOf implementation when changing the
+/// contents of this struct.
+#[derive(Clone, Debug, Default, JSTraceable)]
+pub(crate) struct Animations {
+ pub sets: Arc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>,
+ have_running_animations: bool,
+}
+
+impl Animations {
+ pub(crate) fn new() -> Self {
+ Animations {
+ sets: Default::default(),
+ have_running_animations: false,
+ }
+ }
+
+ /// Processes any new animations that were discovered after reflow. Collect messages
+ /// that trigger events for any animations that changed state.
+ /// TODO(mrobinson): The specification dictates that this should happen before reflow.
+ pub(crate) fn do_post_reflow_update(&mut self, window: &Window, now: f64) -> AnimationsUpdate {
+ let mut update = AnimationsUpdate::new(window.pipeline_id());
+
+ {
+ let mut sets = self.sets.write();
+ update.collect_newly_animating_nodes(&sets);
+
+ for set in sets.values_mut() {
+ Self::handle_canceled_animations(set, now, &mut update);
+ Self::finish_running_animations(set, now, &mut update);
+ Self::handle_new_animations(set, &mut update);
+ }
+
+ // Remove empty states from our collection of states in order to free
+ // up space as soon as we are no longer tracking any animations for
+ // a node.
+ sets.retain(|_, state| !state.is_empty());
+ }
+
+ self.update_running_animations_presence(window);
+
+ update
+ }
+
+ pub(crate) fn running_animation_count(&self) -> usize {
+ self.sets
+ .read()
+ .values()
+ .map(|state| state.running_animation_and_transition_count())
+ .sum()
+ }
+
+ fn update_running_animations_presence(&mut self, window: &Window) {
+ let have_running_animations = self
+ .sets
+ .read()
+ .values()
+ .any(|state| state.needs_animation_ticks());
+ if have_running_animations == self.have_running_animations {
+ return;
+ }
+
+ self.have_running_animations = have_running_animations;
+ let state = match have_running_animations {
+ true => AnimationsPresentState::AnimationsPresent,
+ false => AnimationsPresentState::NoAnimationsPresent,
+ };
+
+ window.send_to_constellation(ScriptMsg::ChangeRunningAnimationsState(state));
+ }
+
+ /// Walk through the list of running animations and remove all of the ones that
+ /// have ended.
+ fn finish_running_animations(
+ set: &mut ElementAnimationSet,
+ now: f64,
+ update: &mut AnimationsUpdate,
+ ) {
+ for animation in set.animations.iter_mut() {
+ if animation.state == AnimationState::Running && animation.has_ended(now) {
+ animation.state = AnimationState::Finished;
+ update.add_event(
+ animation.node,
+ animation.name.to_string(),
+ TransitionOrAnimationEventType::AnimationEnd,
+ animation.active_duration(),
+ );
+ }
+ }
+
+ for transition in set.transitions.iter_mut() {
+ if transition.state == AnimationState::Running && transition.has_ended(now) {
+ transition.state = AnimationState::Finished;
+ update.add_event(
+ transition.node,
+ transition.property_animation.property_name().into(),
+ TransitionOrAnimationEventType::TransitionEnd,
+ transition.property_animation.duration,
+ );
+ }
+ }
+ }
+
+ /// Send events for canceled animations. Currently this only handles canceled
+ /// transitions, but eventually this should handle canceled CSS animations as
+ /// well.
+ fn handle_canceled_animations(
+ set: &mut ElementAnimationSet,
+ now: f64,
+ update: &mut AnimationsUpdate,
+ ) {
+ for transition in &set.transitions {
+ if transition.state == AnimationState::Canceled {
+ // TODO(mrobinson): We need to properly compute the elapsed_time here
+ // according to https://drafts.csswg.org/css-transitions/#event-transitionevent
+ update.add_event(
+ transition.node,
+ transition.property_animation.property_name().into(),
+ TransitionOrAnimationEventType::TransitionCancel,
+ (now - transition.start_time).max(0.),
+ );
+ }
+ }
+
+ // TODO(mrobinson): We need to send animationcancel events.
+ set.clear_canceled_animations();
+ }
+
+ fn handle_new_animations(set: &mut ElementAnimationSet, update: &mut AnimationsUpdate) {
+ for animation in set.animations.iter_mut() {
+ animation.is_new = false;
+ }
+
+ for transition in set.transitions.iter_mut() {
+ if transition.is_new {
+ // TODO(mrobinson): We need to properly compute the elapsed_time here
+ // according to https://drafts.csswg.org/css-transitions/#event-transitionevent
+ update.add_event(
+ transition.node,
+ transition.property_animation.property_name().into(),
+ TransitionOrAnimationEventType::TransitionRun,
+ 0.,
+ );
+ transition.is_new = false;
+ }
+ }
+ }
+}
+
+impl MallocSizeOf for Animations {
+ fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ self.sets.read().size_of(ops) + self.have_running_animations.size_of(ops)
+ }
+}
+
+pub(crate) struct AnimationsUpdate {
+ pub pipeline_id: PipelineId,
+ pub events: Vec<TransitionOrAnimationEvent>,
+ pub newly_animating_nodes: Vec<UntrustedNodeAddress>,
+}
+
+impl AnimationsUpdate {
+ fn new(pipeline_id: PipelineId) -> Self {
+ AnimationsUpdate {
+ pipeline_id,
+ events: Default::default(),
+ newly_animating_nodes: Default::default(),
+ }
+ }
+
+ fn add_event(
+ &mut self,
+ node: OpaqueNode,
+ property_or_animation_name: String,
+ event_type: TransitionOrAnimationEventType,
+ elapsed_time: f64,
+ ) {
+ let node = UntrustedNodeAddress(node.0 as *const c_void);
+ self.events.push(TransitionOrAnimationEvent {
+ pipeline_id: self.pipeline_id,
+ event_type,
+ node,
+ property_or_animation_name,
+ elapsed_time,
+ });
+ }
+
+ pub(crate) fn is_empty(&self) -> bool {
+ self.events.is_empty() && self.newly_animating_nodes.is_empty()
+ }
+
+ /// Collect newly animating nodes, which is used by the script process during
+ /// forced, synchronous reflows to root DOM nodes for the duration of their
+ /// animations or transitions.
+ /// TODO(mrobinson): Look into handling the rooting inside this class.
+ fn collect_newly_animating_nodes(
+ &mut self,
+ animation_states: &FxHashMap<OpaqueNode, ElementAnimationSet>,
+ ) {
+ // This extends the output vector with an iterator that contains a copy of the node
+ // address for every new animation. The script thread currently stores a rooted node
+ // for every property that is transitioning. The current strategy of repeating the
+ // node address is a holdover from when the code here looked different.
+ self.newly_animating_nodes
+ .extend(animation_states.iter().flat_map(|(node, state)| {
+ let mut num_new_animations = state
+ .animations
+ .iter()
+ .filter(|animation| animation.is_new)
+ .count();
+ num_new_animations += state
+ .transitions
+ .iter()
+ .filter(|transition| transition.is_new)
+ .count();
+
+ let node = UntrustedNodeAddress(node.0 as *const c_void);
+ std::iter::repeat(node).take(num_new_animations)
+ }));
+ }
+}
+
+/// The type of transition event to trigger. These are defined by
+/// CSS Transitions § 6.1 and CSS Animations § 4.2
+#[derive(Clone, Debug, Deserialize, JSTraceable, Serialize)]
+pub enum TransitionOrAnimationEventType {
+ /// "The transitionrun event occurs when a transition is created (i.e., when it
+ /// is added to the set of running transitions)."
+ TransitionRun,
+ /// "The transitionend event occurs at the completion of the transition. In the
+ /// case where a transition is removed before completion, such as if the
+ /// transition-property is removed, then the event will not fire."
+ TransitionEnd,
+ /// "The transitioncancel event occurs when a transition is canceled."
+ TransitionCancel,
+ /// "The animationend event occurs when the animation finishes"
+ AnimationEnd,
+}
+
+impl TransitionOrAnimationEventType {
+ /// Whether or not this event finalizes the animation or transition. During finalization
+ /// the DOM object associated with this transition or animation is unrooted.
+ pub fn finalizes_transition_or_animation(&self) -> bool {
+ match *self {
+ Self::TransitionEnd | Self::TransitionCancel | Self::AnimationEnd => true,
+ Self::TransitionRun => false,
+ }
+ }
+
+ /// Whether or not this event is a transition-related event.
+ pub fn is_transition_event(&self) -> bool {
+ match *self {
+ Self::TransitionRun | Self::TransitionEnd | Self::TransitionCancel => true,
+ Self::AnimationEnd => false,
+ }
+ }
+}
+
+#[derive(Deserialize, JSTraceable, Serialize)]
+/// A transition or animation event.
+pub struct TransitionOrAnimationEvent {
+ /// The pipeline id of the layout task that sent this message.
+ pub pipeline_id: PipelineId,
+ /// The type of transition event this should trigger.
+ pub event_type: TransitionOrAnimationEventType,
+ /// The address of the node which owns this transition.
+ pub node: UntrustedNodeAddress,
+ /// The name of the property that is transitioning (in the case of a transition)
+ /// or the name of the animation (in the case of an animation).
+ pub property_or_animation_name: String,
+ /// The elapsed time property to send with this transition event.
+ pub elapsed_time: f64,
+}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index c2543cc984b..26db47233f9 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -93,6 +93,7 @@ use net_traits::response::HttpsState;
use net_traits::response::{Response, ResponseBody};
use net_traits::storage_thread::StorageType;
use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads};
+use parking_lot::RwLock;
use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_layout_interface::message::PendingRestyle;
@@ -100,9 +101,11 @@ use script_layout_interface::rpc::LayoutRPC;
use script_layout_interface::StyleAndOpaqueLayoutData;
use script_traits::serializable::BlobImpl;
use script_traits::transferable::MessagePortImpl;
-use script_traits::{DocumentActivity, DrawAPaintImageResult};
-use script_traits::{MediaSessionActionType, ScriptToConstellationChan, TimerEventId, TimerSource};
-use script_traits::{UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData, WindowSizeType};
+use script_traits::{
+ DocumentActivity, DrawAPaintImageResult, MediaSessionActionType, ScriptToConstellationChan,
+ TimerEventId, TimerSource, UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData,
+ WindowSizeType,
+};
use selectors::matching::ElementSelectorFlags;
use serde::{Deserialize, Serialize};
use servo_arc::Arc as ServoArc;
@@ -131,6 +134,7 @@ use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{Arc, Mutex};
use std::time::{Instant, SystemTime};
+use style::animation::ElementAnimationSet;
use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
use style::author_styles::AuthorStyles;
use style::context::QuirksMode;
@@ -172,7 +176,6 @@ unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>, Box<dyn EventLoopWaker>);
unsafe_no_jsmanaged_fields!(MessagePortImpl);
unsafe_no_jsmanaged_fields!(MessagePortId);
-unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>);
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId);
@@ -184,8 +187,7 @@ unsafe_no_jsmanaged_fields!(CSSError);
unsafe_no_jsmanaged_fields!(&'static Encoding);
-unsafe_no_jsmanaged_fields!(RefCell<Decoder>);
-unsafe_no_jsmanaged_fields!(RefCell<Vec<u8>>);
+unsafe_no_jsmanaged_fields!(Decoder);
unsafe_no_jsmanaged_fields!(Reflector);
@@ -252,6 +254,12 @@ unsafe impl<T: JSTraceable> JSTraceable for ServoArc<T> {
}
}
+unsafe impl<T: JSTraceable> JSTraceable for RwLock<T> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ self.read().trace(trc)
+ }
+}
+
unsafe impl<T: JSTraceable + ?Sized> JSTraceable for Box<T> {
unsafe fn trace(&self, trc: *mut JSTracer) {
(**self).trace(trc)
@@ -284,6 +292,12 @@ unsafe impl<T: JSTraceable> JSTraceable for DomRefCell<T> {
}
}
+unsafe impl<T: JSTraceable> JSTraceable for RefCell<T> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ (*self).borrow().trace(trc)
+ }
+}
+
unsafe impl JSTraceable for Heap<*mut JSObject> {
unsafe fn trace(&self, trc: *mut JSTracer) {
if self.get().is_null() {
@@ -530,8 +544,7 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId);
unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
unsafe_no_jsmanaged_fields!(WebGLVersion);
unsafe_no_jsmanaged_fields!(WebGLSLVersion);
-unsafe_no_jsmanaged_fields!(RefCell<Option<WebGPU>>);
-unsafe_no_jsmanaged_fields!(RefCell<Identities>);
+unsafe_no_jsmanaged_fields!(Identities);
unsafe_no_jsmanaged_fields!(WebGPU);
unsafe_no_jsmanaged_fields!(WebGPUAdapter);
unsafe_no_jsmanaged_fields!(WebGPUBuffer);
@@ -544,7 +557,7 @@ unsafe_no_jsmanaged_fields!(WebGPUShaderModule);
unsafe_no_jsmanaged_fields!(WebGPUCommandBuffer);
unsafe_no_jsmanaged_fields!(WebGPUCommandEncoder);
unsafe_no_jsmanaged_fields!(WebGPUDevice);
-unsafe_no_jsmanaged_fields!(RefCell<Option<RawPass>>);
+unsafe_no_jsmanaged_fields!(Option<RawPass>);
unsafe_no_jsmanaged_fields!(GPUBufferState);
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
unsafe_no_jsmanaged_fields!(MediaList);
@@ -586,6 +599,7 @@ unsafe_no_jsmanaged_fields!(MediaSessionActionType);
unsafe_no_jsmanaged_fields!(MediaMetadata);
unsafe_no_jsmanaged_fields!(WebrenderIpcSender);
unsafe_no_jsmanaged_fields!(StreamConsumer);
+unsafe_no_jsmanaged_fields!(ElementAnimationSet);
unsafe impl<'a> JSTraceable for &'a str {
#[inline]
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 5e40f433da5..86c5b1caaa1 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::animation_timeline::AnimationTimeline;
+use crate::animations::{Animations, AnimationsUpdate};
use crate::document_loader::{DocumentLoader, LoadType};
use crate::dom::attr::Attr;
use crate::dom::beforeunloadevent::BeforeUnloadEvent;
@@ -384,6 +385,8 @@ pub struct Document {
/// A timeline for animations which is used for synchronizing animations.
/// https://drafts.csswg.org/web-animations/#timeline
animation_timeline: DomRefCell<AnimationTimeline>,
+ /// Animations for this Document
+ animations: DomRefCell<Animations>,
}
#[derive(JSTraceable, MallocSizeOf)]
@@ -2913,6 +2916,7 @@ impl Document {
} else {
DomRefCell::new(AnimationTimeline::new())
},
+ animations: DomRefCell::new(Animations::new()),
}
}
@@ -3615,17 +3619,27 @@ impl Document {
.collect()
}
- pub fn advance_animation_timeline_for_testing(&self, delta: f64) {
+ pub(crate) fn advance_animation_timeline_for_testing(&self, delta: f64) {
self.animation_timeline.borrow_mut().advance_specific(delta);
}
- pub fn update_animation_timeline(&self) {
+ pub(crate) fn update_animation_timeline(&self) {
self.animation_timeline.borrow_mut().update();
}
- pub fn current_animation_timeline_value(&self) -> f64 {
+ pub(crate) fn current_animation_timeline_value(&self) -> f64 {
self.animation_timeline.borrow().current_value()
}
+
+ pub(crate) fn animations(&self) -> Ref<Animations> {
+ self.animations.borrow()
+ }
+
+ pub(crate) fn update_animations(&self) -> AnimationsUpdate {
+ self.animations
+ .borrow_mut()
+ .do_post_reflow_update(&self.window, self.current_animation_timeline_value())
+ }
}
impl Element {
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 414ef0f2e45..cbf19039605 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -81,7 +81,7 @@ use dom_struct::dom_struct;
use embedder_traits::{EmbedderMsg, EventLoopWaker, PromptDefinition, PromptOrigin, PromptResult};
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
-use ipc_channel::ipc::{channel, IpcSender};
+use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER;
use js::jsapi::Heap;
use js::jsapi::JSAutoRealm;
@@ -1305,9 +1305,9 @@ impl WindowMethods for Window {
}
fn RunningAnimationCount(&self) -> u32 {
- let (sender, receiver) = channel().unwrap();
- let _ = self.layout_chan.send(Msg::GetRunningAnimations(sender));
- receiver.recv().unwrap_or(0) as u32
+ self.document
+ .get()
+ .map_or(0, |d| d.animations().running_animation_count() as u32)
}
// https://html.spec.whatwg.org/multipage/#dom-name
@@ -1643,6 +1643,7 @@ impl Window {
dom_count: document.dom_count(),
pending_restyles: document.drain_pending_restyles(),
animation_timeline_value: document.current_animation_timeline_value(),
+ animations: document.animations().sets.clone(),
};
self.layout_chan
@@ -1706,8 +1707,9 @@ impl Window {
}
}
+ let update = document.update_animations();
unsafe {
- ScriptThread::note_newly_animating_nodes(pipeline_id, complete.newly_animating_nodes);
+ ScriptThread::process_animations_update(update);
}
true
diff --git a/components/script/lib.rs b/components/script/lib.rs
index cd73045456a..886a76e9c95 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -48,6 +48,7 @@ extern crate servo_atoms;
extern crate style;
mod animation_timeline;
+mod animations;
#[warn(deprecated)]
#[macro_use]
mod task;
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 008274ded47..9754aa65ede 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -17,6 +17,9 @@
//! a page runs its course and the script thread returns to processing events in the main event
//! loop.
+use crate::animations::{
+ AnimationsUpdate, TransitionOrAnimationEvent, TransitionOrAnimationEventType,
+};
use crate::devtools;
use crate::document_loader::DocumentLoader;
use crate::dom::animationevent::AnimationEvent;
@@ -141,8 +144,8 @@ use script_traits::{
LayoutMsg, LoadData, LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType,
NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory,
ScriptToConstellationChan, StructuredSerializedData, TimerSchedulerMsg, TouchEventType,
- TouchId, TransitionOrAnimationEvent, TransitionOrAnimationEventType, UntrustedNodeAddress,
- UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta, WindowSizeData, WindowSizeType,
+ TouchId, UntrustedNodeAddress, UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta,
+ WindowSizeData, WindowSizeType,
};
use servo_atoms::Atom;
use servo_config::opts;
@@ -510,10 +513,8 @@ impl<'a> Iterator for DocumentsIter<'a> {
// thread during parsing. For this reason, we don't trace the
// incomplete parser contexts during GC.
type IncompleteParserContexts = Vec<(PipelineId, ParserContext)>;
-unsafe_no_jsmanaged_fields!(RefCell<IncompleteParserContexts>);
unsafe_no_jsmanaged_fields!(TaskQueue<MainThreadScriptMsg>);
-
unsafe_no_jsmanaged_fields!(dyn BackgroundHangMonitorRegister);
unsafe_no_jsmanaged_fields!(dyn BackgroundHangMonitor);
@@ -644,6 +645,9 @@ pub struct ScriptThread {
/// of the transition.
animating_nodes: DomRefCell<HashMap<PipelineId, Vec<Dom<Node>>>>,
+ /// Animations events that are pending to be sent.
+ animation_events: RefCell<Vec<TransitionOrAnimationEvent>>,
+
/// <https://html.spec.whatwg.org/multipage/#custom-element-reactions-stack>
custom_element_reaction_stack: CustomElementReactionStack,
@@ -826,20 +830,35 @@ impl ScriptThread {
})
}
- pub unsafe fn note_newly_animating_nodes(
- pipeline_id: PipelineId,
- nodes: Vec<UntrustedNodeAddress>,
- ) {
+ /// Consume the list of pointer addresses corresponding to DOM nodes that are animating
+ /// and root them in a per-pipeline list of nodes.
+ ///
+ /// Unsafety: any pointer to invalid memory (ie. a GCed node) will trigger a crash.
+ /// TODO: ensure caller uses rooted nodes instead of unsafe node addresses.
+ pub(crate) unsafe fn process_animations_update(mut update: AnimationsUpdate) {
+ if update.is_empty() {
+ return;
+ }
+
SCRIPT_THREAD_ROOT.with(|root| {
let script_thread = &*root.get().unwrap();
+
+ if !update.events.is_empty() {
+ script_thread
+ .animation_events
+ .borrow_mut()
+ .append(&mut update.events);
+ }
+
let js_runtime = script_thread.js_runtime.rt();
- let new_nodes = nodes
+ let new_nodes = update
+ .newly_animating_nodes
.into_iter()
.map(|n| Dom::from_ref(&*from_untrusted_node_address(js_runtime, n)));
script_thread
.animating_nodes
.borrow_mut()
- .entry(pipeline_id)
+ .entry(update.pipeline_id)
.or_insert_with(Vec::new)
.extend(new_nodes);
})
@@ -1354,6 +1373,7 @@ impl ScriptThread {
docs_with_no_blocking_loads: Default::default(),
animating_nodes: Default::default(),
+ animation_events: Default::default(),
custom_element_reaction_stack: CustomElementReactionStack::new(),
@@ -1620,9 +1640,8 @@ impl ScriptThread {
}
// Perform step 11.10 from https://html.spec.whatwg.org/multipage/#event-loops.
- // TODO(mrobinson): This should also update the current animations and send events
- // to conform to the HTML specification. This might mean having events for rooting
- // DOM nodes and ultimately all animations living in script.
+ // TODO(mrobinson): This should also update the current animations to conform to
+ // the HTML specification.
fn update_animations_and_send_events(&self) {
for (_, document) in self.documents.borrow().iter() {
// Only update the time if it isn't being managed by a test.
@@ -1630,6 +1649,13 @@ impl ScriptThread {
document.update_animation_timeline();
}
}
+
+ // We remove the events because handling these events might trigger
+ // a reflow which might want to add more events to the queue.
+ let events = self.animation_events.replace(Vec::new());
+ for event in events.into_iter() {
+ self.handle_transition_or_animation_event(&event);
+ }
}
fn categorize_msg(&self, msg: &MixedMessage) -> ScriptThreadEventCategory {
@@ -1720,7 +1746,6 @@ impl ScriptThread {
FocusIFrame(id, ..) => Some(id),
WebDriverScriptCommand(id, ..) => Some(id),
TickAllAnimations(id, ..) => Some(id),
- TransitionOrAnimationEvent { .. } => None,
WebFontLoaded(id) => Some(id),
DispatchIFrameLoadEvent {
target: _,
@@ -1921,9 +1946,6 @@ impl ScriptThread {
ConstellationControlMsg::TickAllAnimations(pipeline_id, tick_type) => {
self.handle_tick_all_animations(pipeline_id, tick_type)
},
- ConstellationControlMsg::TransitionOrAnimationEvent(ref event) => {
- self.handle_transition_or_animation_event(event);
- },
ConstellationControlMsg::WebFontLoaded(pipeline_id) => {
self.handle_web_font_loaded(pipeline_id)
},
diff --git a/components/script_layout_interface/Cargo.toml b/components/script_layout_interface/Cargo.toml
index 8f759970c62..80724ac1295 100644
--- a/components/script_layout_interface/Cargo.toml
+++ b/components/script_layout_interface/Cargo.toml
@@ -16,6 +16,7 @@ atomic_refcell = "0.1"
canvas_traits = {path = "../canvas_traits"}
crossbeam-channel = "0.4"
euclid = "0.20"
+fxhash = "0.2"
gfx_traits = {path = "../gfx_traits"}
html5ever = "0.25"
ipc-channel = "0.14"
@@ -25,6 +26,7 @@ malloc_size_of_derive = "0.1"
metrics = {path = "../metrics"}
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
+parking_lot = "0.9"
profile_traits = {path = "../profile_traits"}
range = {path = "../range"}
script_traits = {path = "../script_traits"}
diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs
index ac5c0c59ba0..1dcbfe7eb33 100644
--- a/components/script_layout_interface/message.rs
+++ b/components/script_layout_interface/message.rs
@@ -7,20 +7,25 @@ use crate::{PendingImage, TrustedNodeAddress};
use app_units::Au;
use crossbeam_channel::{Receiver, Sender};
use euclid::default::{Point2D, Rect};
+use fxhash::FxHashMap;
use gfx_traits::Epoch;
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use metrics::PaintTimeMetrics;
use msg::constellation_msg::{BackgroundHangMonitorRegister, BrowsingContextId, PipelineId};
use net_traits::image_cache::ImageCache;
+use parking_lot::RwLock;
use profile_traits::mem::ReportsChan;
use script_traits::Painter;
-use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
-use script_traits::{ScrollState, UntrustedNodeAddress, WindowSizeData};
+use script_traits::{
+ ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, ScrollState,
+ WindowSizeData,
+};
use servo_arc::Arc as ServoArc;
use servo_atoms::Atom;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
+use style::animation::ElementAnimationSet;
use style::context::QuirksMode;
use style::dom::OpaqueNode;
use style::invalidation::element::restyle_hints::RestyleHint;
@@ -87,9 +92,6 @@ pub enum Msg {
/// Send to layout the precise time when the navigation started.
SetNavigationStart(u64),
-
- /// Request the current number of animations that are running.
- GetRunningAnimations(IpcSender<usize>),
}
#[derive(Debug, PartialEq)]
@@ -183,8 +185,6 @@ pub struct Reflow {
pub struct ReflowComplete {
/// The list of images that were encountered that are in progress.
pub pending_images: Vec<PendingImage>,
- /// The list of nodes that initiated a CSS transition.
- pub newly_animating_nodes: Vec<UntrustedNodeAddress>,
}
/// Information needed for a script-initiated reflow.
@@ -209,6 +209,8 @@ pub struct ScriptReflow {
pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
/// The current animation timeline value.
pub animation_timeline_value: f64,
+ /// The set of animations for this document.
+ pub animations: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>,
}
pub struct LayoutThreadInit {
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index aeac1ff6087..fc3b19d2b97 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -282,58 +282,6 @@ pub enum UpdatePipelineIdReason {
Traversal,
}
-/// The type of transition event to trigger. These are defined by
-/// CSS Transitions § 6.1 and CSS Animations § 4.2
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum TransitionOrAnimationEventType {
- /// "The transitionrun event occurs when a transition is created (i.e., when it
- /// is added to the set of running transitions)."
- TransitionRun,
- /// "The transitionend event occurs at the completion of the transition. In the
- /// case where a transition is removed before completion, such as if the
- /// transition-property is removed, then the event will not fire."
- TransitionEnd,
- /// "The transitioncancel event occurs when a transition is canceled."
- TransitionCancel,
- /// "The animationend event occurs when the animation finishes"
- AnimationEnd,
-}
-
-impl TransitionOrAnimationEventType {
- /// Whether or not this event finalizes the animation or transition. During finalization
- /// the DOM object associated with this transition or animation is unrooted.
- pub fn finalizes_transition_or_animation(&self) -> bool {
- match *self {
- Self::TransitionEnd | Self::TransitionCancel | Self::AnimationEnd => true,
- Self::TransitionRun => false,
- }
- }
-
- /// Whether or not this event is a transition-related event.
- pub fn is_transition_event(&self) -> bool {
- match *self {
- Self::TransitionRun | Self::TransitionEnd | Self::TransitionCancel => true,
- Self::AnimationEnd => false,
- }
- }
-}
-
-#[derive(Deserialize, Serialize)]
-/// A transition or animation event.
-pub struct TransitionOrAnimationEvent {
- /// The pipeline id of the layout task that sent this message.
- pub pipeline_id: PipelineId,
- /// The type of transition event this should trigger.
- pub event_type: TransitionOrAnimationEventType,
- /// The address of the node which owns this transition.
- pub node: UntrustedNodeAddress,
- /// The name of the property that is transitioning (in the case of a transition)
- /// or the name of the animation (in the case of an animation).
- pub property_or_animation_name: String,
- /// The elapsed time property to send with this transition event.
- pub elapsed_time: f64,
-}
-
/// Messages sent from the constellation or layout to the script thread.
#[derive(Deserialize, Serialize)]
pub enum ConstellationControlMsg {
@@ -420,8 +368,6 @@ pub enum ConstellationControlMsg {
WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
/// Notifies script thread that all animations are done
TickAllAnimations(PipelineId, AnimationTickType),
- /// Notifies the script thread that a transition or animation related event should be sent.
- TransitionOrAnimationEvent(TransitionOrAnimationEvent),
/// Notifies the script thread that a new Web font has been loaded, and thus the page should be
/// reflowed.
WebFontLoaded(PipelineId),
@@ -481,7 +427,6 @@ impl fmt::Debug for ConstellationControlMsg {
FocusIFrame(..) => "FocusIFrame",
WebDriverScriptCommand(..) => "WebDriverScriptCommand",
TickAllAnimations(..) => "TickAllAnimations",
- TransitionOrAnimationEvent { .. } => "TransitionOrAnimationEvent",
WebFontLoaded(..) => "WebFontLoaded",
DispatchIFrameLoadEvent { .. } => "DispatchIFrameLoadEvent",
DispatchStorageEvent(..) => "DispatchStorageEvent",
diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs
index 245a02ec6d2..b5687d6217e 100644
--- a/components/script_traits/script_msg.rs
+++ b/components/script_traits/script_msg.rs
@@ -61,8 +61,6 @@ pub struct IFrameSizeMsg {
/// Messages from the layout to the constellation.
#[derive(Deserialize, Serialize)]
pub enum LayoutMsg {
- /// Indicates whether this pipeline is currently running animations.
- ChangeRunningAnimationsState(PipelineId, AnimationState),
/// Inform the constellation of the size of the iframe's viewport.
IFrameSizes(Vec<IFrameSizeMsg>),
/// Requests that the constellation inform the compositor that it needs to record
@@ -76,7 +74,6 @@ impl fmt::Debug for LayoutMsg {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
use self::LayoutMsg::*;
let variant = match *self {
- ChangeRunningAnimationsState(..) => "ChangeRunningAnimationsState",
IFrameSizes(..) => "IFrameSizes",
PendingPaintMetric(..) => "PendingPaintMetric",
ViewportConstrained(..) => "ViewportConstrained",
diff --git a/components/style/animation.rs b/components/style/animation.rs
index e928d81de88..f76759abdfb 100644
--- a/components/style/animation.rs
+++ b/components/style/animation.rs
@@ -27,7 +27,7 @@ use servo_arc::Arc;
use std::fmt;
/// Represents an animation for a given property.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, MallocSizeOf)]
pub struct PropertyAnimation {
/// An `AnimatedProperty` that this `PropertyAnimation` corresponds to.
property: AnimatedProperty,
@@ -136,7 +136,7 @@ impl PropertyAnimation {
}
/// This structure represents the state of an animation.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
pub enum AnimationState {
/// This animation is paused. The inner field is the percentage of progress
/// when it was paused, from 0 to 1.
@@ -153,7 +153,7 @@ pub enum AnimationState {
///
/// If the iteration count is infinite, there's no other state, otherwise we
/// have to keep track the current iteration and the max iteration count.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, MallocSizeOf)]
pub enum KeyframesIterationState {
/// Infinite iterations, so no need to track a state.
Infinite,
@@ -164,7 +164,7 @@ pub enum KeyframesIterationState {
}
/// A CSS Animation
-#[derive(Clone)]
+#[derive(Clone, MallocSizeOf)]
pub struct Animation {
/// The node associated with this animation.
pub node: OpaqueNode,
@@ -198,6 +198,7 @@ pub struct Animation {
/// The original cascade style, needed to compute the generated keyframes of
/// the animation.
+ #[ignore_malloc_size_of = "ComputedValues"]
pub cascade_style: Arc<ComputedValues>,
/// Whether or not this animation is new and or has already been tracked
@@ -542,7 +543,7 @@ impl fmt::Debug for Animation {
}
/// A CSS Transition
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, MallocSizeOf)]
pub struct Transition {
/// The node associated with this animation.
pub node: OpaqueNode,
@@ -597,7 +598,7 @@ impl Transition {
}
/// Holds the animation state for a particular element.
-#[derive(Debug, Default)]
+#[derive(Debug, Default, MallocSizeOf)]
pub struct ElementAnimationSet {
/// The animations for this element.
pub animations: Vec<Animation>,