mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #26464 - mrobinson:move-animations-to-script, r=jdm
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. <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes do not require tests because they should not change behavior. <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
commit
c5617efff0
21 changed files with 444 additions and 371 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -4562,6 +4562,7 @@ dependencies = [
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
"euclid",
|
"euclid",
|
||||||
"fnv",
|
"fnv",
|
||||||
|
"fxhash",
|
||||||
"headers",
|
"headers",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"http",
|
"http",
|
||||||
|
@ -4641,6 +4642,7 @@ dependencies = [
|
||||||
"canvas_traits",
|
"canvas_traits",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"euclid",
|
"euclid",
|
||||||
|
"fxhash",
|
||||||
"gfx_traits",
|
"gfx_traits",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
|
@ -4650,6 +4652,7 @@ dependencies = [
|
||||||
"metrics",
|
"metrics",
|
||||||
"msg",
|
"msg",
|
||||||
"net_traits",
|
"net_traits",
|
||||||
|
"parking_lot",
|
||||||
"profile_traits",
|
"profile_traits",
|
||||||
"range",
|
"range",
|
||||||
"script_traits",
|
"script_traits",
|
||||||
|
|
|
@ -2237,9 +2237,6 @@ where
|
||||||
fn handle_request_from_layout(&mut self, message: FromLayoutMsg) {
|
fn handle_request_from_layout(&mut self, message: FromLayoutMsg) {
|
||||||
debug!("Constellation got {:?} message", message);
|
debug!("Constellation got {:?} message", message);
|
||||||
match 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
|
// Layout sends new sizes for all subframes. This needs to be reflected by all
|
||||||
// frame trees in the navigation context containing the subframe.
|
// frame trees in the navigation context containing the subframe.
|
||||||
FromLayoutMsg::IFrameSizes(iframe_sizes) => {
|
FromLayoutMsg::IFrameSizes(iframe_sizes) => {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,7 +17,6 @@ use net_traits::image_cache::{
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use script_layout_interface::{PendingImage, PendingImageState};
|
use script_layout_interface::{PendingImage, PendingImageState};
|
||||||
use script_traits::Painter;
|
use script_traits::Painter;
|
||||||
use script_traits::UntrustedNodeAddress;
|
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use std::cell::{RefCell, RefMut};
|
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.
|
/// A list of in-progress image loads to be shared with the script thread.
|
||||||
pub pending_images: Mutex<Vec<PendingImage>>,
|
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> {
|
impl<'a> Drop for LayoutContext<'a> {
|
||||||
|
|
|
@ -20,7 +20,6 @@ extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod layout_debug;
|
pub mod layout_debug;
|
||||||
|
|
||||||
pub mod animation;
|
|
||||||
mod block;
|
mod block;
|
||||||
pub mod construct;
|
pub mod construct;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
|
|
@ -29,7 +29,7 @@ use crossbeam_channel::{Receiver, Sender};
|
||||||
use embedder_traits::resources::{self, Resource};
|
use embedder_traits::resources::{self, Resource};
|
||||||
use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D};
|
use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use fxhash::FxHashMap;
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use gfx::font;
|
use gfx::font;
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use gfx::font_context;
|
use gfx::font_context;
|
||||||
|
@ -37,7 +37,6 @@ use gfx_traits::{node_id_from_scroll_id, Epoch};
|
||||||
use histogram::Histogram;
|
use histogram::Histogram;
|
||||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use layout::animation;
|
|
||||||
use layout::construct::ConstructionResult;
|
use layout::construct::ConstructionResult;
|
||||||
use layout::context::malloc_size_of_persistent_local_context;
|
use layout::context::malloc_size_of_persistent_local_context;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
|
@ -191,9 +190,6 @@ pub struct LayoutThread {
|
||||||
/// The document-specific shared lock used for author-origin stylesheets
|
/// The document-specific shared lock used for author-origin stylesheets
|
||||||
document_shared_lock: Option<SharedRwLock>,
|
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
|
/// A counter for epoch messages
|
||||||
epoch: Cell<Epoch>,
|
epoch: Cell<Epoch>,
|
||||||
|
|
||||||
|
@ -552,7 +548,6 @@ impl LayoutThread {
|
||||||
outstanding_web_fonts: Arc::new(AtomicUsize::new(0)),
|
outstanding_web_fonts: Arc::new(AtomicUsize::new(0)),
|
||||||
root_flow: RefCell::new(None),
|
root_flow: RefCell::new(None),
|
||||||
document_shared_lock: 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 starts at 1 because of the initial display list for epoch 0 that we send to WR
|
||||||
epoch: Cell::new(Epoch(1)),
|
epoch: Cell::new(Epoch(1)),
|
||||||
viewport_size: Size2D::new(Au(0), Au(0)),
|
viewport_size: Size2D::new(Au(0), Au(0)),
|
||||||
|
@ -613,6 +608,7 @@ impl LayoutThread {
|
||||||
snapshot_map: &'a SnapshotMap,
|
snapshot_map: &'a SnapshotMap,
|
||||||
origin: ImmutableOrigin,
|
origin: ImmutableOrigin,
|
||||||
animation_timeline_value: f64,
|
animation_timeline_value: f64,
|
||||||
|
animation_states: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>,
|
||||||
) -> LayoutContext<'a> {
|
) -> LayoutContext<'a> {
|
||||||
LayoutContext {
|
LayoutContext {
|
||||||
id: self.id,
|
id: self.id,
|
||||||
|
@ -622,7 +618,7 @@ impl LayoutThread {
|
||||||
options: GLOBAL_STYLE_DATA.options.clone(),
|
options: GLOBAL_STYLE_DATA.options.clone(),
|
||||||
guards,
|
guards,
|
||||||
visited_styles_enabled: false,
|
visited_styles_enabled: false,
|
||||||
animation_states: self.animation_states.clone(),
|
animation_states,
|
||||||
registered_speculative_painters: &self.registered_painters,
|
registered_speculative_painters: &self.registered_painters,
|
||||||
current_time_for_animations: animation_timeline_value,
|
current_time_for_animations: animation_timeline_value,
|
||||||
traversal_flags: TraversalFlags::empty(),
|
traversal_flags: TraversalFlags::empty(),
|
||||||
|
@ -632,7 +628,6 @@ impl LayoutThread {
|
||||||
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
|
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
|
||||||
webrender_image_cache: self.webrender_image_cache.clone(),
|
webrender_image_cache: self.webrender_image_cache.clone(),
|
||||||
pending_images: Mutex::new(vec![]),
|
pending_images: Mutex::new(vec![]),
|
||||||
newly_animating_nodes: Mutex::new(vec![]),
|
|
||||||
registered_painters: &self.registered_painters,
|
registered_painters: &self.registered_painters,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -657,7 +652,6 @@ impl LayoutThread {
|
||||||
},
|
},
|
||||||
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
|
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
|
||||||
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
|
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
|
||||||
Msg::GetRunningAnimations(..) => LayoutHangAnnotation::GetRunningAnimations,
|
|
||||||
};
|
};
|
||||||
self.background_hang_monitor
|
self.background_hang_monitor
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -824,15 +818,6 @@ impl LayoutThread {
|
||||||
Msg::SetNavigationStart(time) => {
|
Msg::SetNavigationStart(time) => {
|
||||||
self.paint_time_metrics.set_navigation_start(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
|
true
|
||||||
|
@ -1413,8 +1398,13 @@ impl LayoutThread {
|
||||||
self.stylist.flush(&guards, Some(element), Some(&map));
|
self.stylist.flush(&guards, Some(element), Some(&map));
|
||||||
|
|
||||||
// Create a layout context for use throughout the following passes.
|
// Create a layout context for use throughout the following passes.
|
||||||
let mut layout_context =
|
let mut layout_context = self.build_layout_context(
|
||||||
self.build_layout_context(guards.clone(), &map, origin, data.animation_timeline_value);
|
guards.clone(),
|
||||||
|
&map,
|
||||||
|
origin,
|
||||||
|
data.animation_timeline_value,
|
||||||
|
data.animations.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let pool;
|
let pool;
|
||||||
let (thread_pool, num_threads) = if self.parallel_flag {
|
let (thread_pool, num_threads) = if self.parallel_flag {
|
||||||
|
@ -1516,8 +1506,6 @@ impl LayoutThread {
|
||||||
) {
|
) {
|
||||||
reflow_result.pending_images =
|
reflow_result.pending_images =
|
||||||
std::mem::replace(&mut *context.pending_images.lock().unwrap(), vec![]);
|
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() {
|
let mut root_flow = match self.root_flow.borrow().clone() {
|
||||||
Some(root_flow) => root_flow,
|
Some(root_flow) => root_flow,
|
||||||
|
@ -1629,6 +1617,33 @@ impl LayoutThread {
|
||||||
rw_data.scroll_offsets = layout_scroll_states
|
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(
|
fn perform_post_style_recalc_layout_passes(
|
||||||
&self,
|
&self,
|
||||||
root_flow: &mut FlowRef,
|
root_flow: &mut FlowRef,
|
||||||
|
@ -1638,18 +1653,10 @@ impl LayoutThread {
|
||||||
rw_data: &mut LayoutThreadData,
|
rw_data: &mut LayoutThreadData,
|
||||||
context: &mut LayoutContext,
|
context: &mut LayoutContext,
|
||||||
) {
|
) {
|
||||||
{
|
Self::cancel_animations_for_nodes_not_in_flow_tree(
|
||||||
let mut newly_animating_nodes = context.newly_animating_nodes.lock().unwrap();
|
&mut *(context.style_context.animation_states.write()),
|
||||||
animation::do_post_style_animations_update(
|
FlowRef::deref_mut(root_flow),
|
||||||
&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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
profile(
|
profile(
|
||||||
profile_time::ProfilerCategory::LayoutRestyleDamagePropagation,
|
profile_time::ProfilerCategory::LayoutRestyleDamagePropagation,
|
||||||
|
|
|
@ -615,7 +615,6 @@ impl LayoutThread {
|
||||||
},
|
},
|
||||||
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
|
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
|
||||||
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
|
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
|
||||||
Msg::GetRunningAnimations(..) => LayoutHangAnnotation::GetRunningAnimations,
|
|
||||||
};
|
};
|
||||||
self.background_hang_monitor
|
self.background_hang_monitor
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -766,9 +765,6 @@ impl LayoutThread {
|
||||||
Msg::SetNavigationStart(time) => {
|
Msg::SetNavigationStart(time) => {
|
||||||
self.paint_time_metrics.set_navigation_start(time);
|
self.paint_time_metrics.set_navigation_start(time);
|
||||||
},
|
},
|
||||||
Msg::GetRunningAnimations(sender) => {
|
|
||||||
let _ = sender.send(0);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
|
@ -541,7 +541,6 @@ pub enum LayoutHangAnnotation {
|
||||||
UpdateScrollStateFromScript,
|
UpdateScrollStateFromScript,
|
||||||
RegisterPaint,
|
RegisterPaint,
|
||||||
SetNavigationStart,
|
SetNavigationStart,
|
||||||
GetRunningAnimations,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||||
|
|
|
@ -53,6 +53,7 @@ encoding_rs = "0.8"
|
||||||
enum-iterator = "0.3"
|
enum-iterator = "0.3"
|
||||||
euclid = "0.20"
|
euclid = "0.20"
|
||||||
fnv = "1.0"
|
fnv = "1.0"
|
||||||
|
fxhash = "0.2"
|
||||||
headers = "0.2"
|
headers = "0.2"
|
||||||
html5ever = "0.25"
|
html5ever = "0.25"
|
||||||
http = "0.1"
|
http = "0.1"
|
||||||
|
|
|
@ -12,7 +12,7 @@ use time;
|
||||||
/// A `AnimationTimeline` which is used to synchronize animations during the script
|
/// A `AnimationTimeline` which is used to synchronize animations during the script
|
||||||
/// event loop.
|
/// event loop.
|
||||||
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
|
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
|
||||||
pub struct AnimationTimeline {
|
pub(crate) struct AnimationTimeline {
|
||||||
current_value: f64,
|
current_value: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
293
components/script/animations.rs
Normal file
293
components/script/animations.rs
Normal file
|
@ -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,
|
||||||
|
}
|
|
@ -93,6 +93,7 @@ use net_traits::response::HttpsState;
|
||||||
use net_traits::response::{Response, ResponseBody};
|
use net_traits::response::{Response, ResponseBody};
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads};
|
use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads};
|
||||||
|
use parking_lot::RwLock;
|
||||||
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
||||||
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
||||||
use script_layout_interface::message::PendingRestyle;
|
use script_layout_interface::message::PendingRestyle;
|
||||||
|
@ -100,9 +101,11 @@ use script_layout_interface::rpc::LayoutRPC;
|
||||||
use script_layout_interface::StyleAndOpaqueLayoutData;
|
use script_layout_interface::StyleAndOpaqueLayoutData;
|
||||||
use script_traits::serializable::BlobImpl;
|
use script_traits::serializable::BlobImpl;
|
||||||
use script_traits::transferable::MessagePortImpl;
|
use script_traits::transferable::MessagePortImpl;
|
||||||
use script_traits::{DocumentActivity, DrawAPaintImageResult};
|
use script_traits::{
|
||||||
use script_traits::{MediaSessionActionType, ScriptToConstellationChan, TimerEventId, TimerSource};
|
DocumentActivity, DrawAPaintImageResult, MediaSessionActionType, ScriptToConstellationChan,
|
||||||
use script_traits::{UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData, WindowSizeType};
|
TimerEventId, TimerSource, UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData,
|
||||||
|
WindowSizeType,
|
||||||
|
};
|
||||||
use selectors::matching::ElementSelectorFlags;
|
use selectors::matching::ElementSelectorFlags;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use servo_arc::Arc as ServoArc;
|
use servo_arc::Arc as ServoArc;
|
||||||
|
@ -131,6 +134,7 @@ use std::rc::Rc;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize};
|
use std::sync::atomic::{AtomicBool, AtomicUsize};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::{Instant, SystemTime};
|
use std::time::{Instant, SystemTime};
|
||||||
|
use style::animation::ElementAnimationSet;
|
||||||
use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
|
use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
|
||||||
use style::author_styles::AuthorStyles;
|
use style::author_styles::AuthorStyles;
|
||||||
use style::context::QuirksMode;
|
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!(MessagePortImpl);
|
||||||
unsafe_no_jsmanaged_fields!(MessagePortId);
|
unsafe_no_jsmanaged_fields!(MessagePortId);
|
||||||
unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>);
|
|
||||||
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
|
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId);
|
unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId);
|
||||||
|
@ -184,8 +187,7 @@ unsafe_no_jsmanaged_fields!(CSSError);
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(&'static Encoding);
|
unsafe_no_jsmanaged_fields!(&'static Encoding);
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(RefCell<Decoder>);
|
unsafe_no_jsmanaged_fields!(Decoder);
|
||||||
unsafe_no_jsmanaged_fields!(RefCell<Vec<u8>>);
|
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(Reflector);
|
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 impl<T: JSTraceable + ?Sized> JSTraceable for Box<T> {
|
||||||
unsafe fn trace(&self, trc: *mut JSTracer) {
|
unsafe fn trace(&self, trc: *mut JSTracer) {
|
||||||
(**self).trace(trc)
|
(**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 impl JSTraceable for Heap<*mut JSObject> {
|
||||||
unsafe fn trace(&self, trc: *mut JSTracer) {
|
unsafe fn trace(&self, trc: *mut JSTracer) {
|
||||||
if self.get().is_null() {
|
if self.get().is_null() {
|
||||||
|
@ -530,8 +544,7 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId);
|
||||||
unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
|
unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
|
||||||
unsafe_no_jsmanaged_fields!(WebGLVersion);
|
unsafe_no_jsmanaged_fields!(WebGLVersion);
|
||||||
unsafe_no_jsmanaged_fields!(WebGLSLVersion);
|
unsafe_no_jsmanaged_fields!(WebGLSLVersion);
|
||||||
unsafe_no_jsmanaged_fields!(RefCell<Option<WebGPU>>);
|
unsafe_no_jsmanaged_fields!(Identities);
|
||||||
unsafe_no_jsmanaged_fields!(RefCell<Identities>);
|
|
||||||
unsafe_no_jsmanaged_fields!(WebGPU);
|
unsafe_no_jsmanaged_fields!(WebGPU);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUAdapter);
|
unsafe_no_jsmanaged_fields!(WebGPUAdapter);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUBuffer);
|
unsafe_no_jsmanaged_fields!(WebGPUBuffer);
|
||||||
|
@ -544,7 +557,7 @@ unsafe_no_jsmanaged_fields!(WebGPUShaderModule);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUCommandBuffer);
|
unsafe_no_jsmanaged_fields!(WebGPUCommandBuffer);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUCommandEncoder);
|
unsafe_no_jsmanaged_fields!(WebGPUCommandEncoder);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUDevice);
|
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!(GPUBufferState);
|
||||||
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
|
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
|
||||||
unsafe_no_jsmanaged_fields!(MediaList);
|
unsafe_no_jsmanaged_fields!(MediaList);
|
||||||
|
@ -586,6 +599,7 @@ unsafe_no_jsmanaged_fields!(MediaSessionActionType);
|
||||||
unsafe_no_jsmanaged_fields!(MediaMetadata);
|
unsafe_no_jsmanaged_fields!(MediaMetadata);
|
||||||
unsafe_no_jsmanaged_fields!(WebrenderIpcSender);
|
unsafe_no_jsmanaged_fields!(WebrenderIpcSender);
|
||||||
unsafe_no_jsmanaged_fields!(StreamConsumer);
|
unsafe_no_jsmanaged_fields!(StreamConsumer);
|
||||||
|
unsafe_no_jsmanaged_fields!(ElementAnimationSet);
|
||||||
|
|
||||||
unsafe impl<'a> JSTraceable for &'a str {
|
unsafe impl<'a> JSTraceable for &'a str {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use crate::animation_timeline::AnimationTimeline;
|
use crate::animation_timeline::AnimationTimeline;
|
||||||
|
use crate::animations::{Animations, AnimationsUpdate};
|
||||||
use crate::document_loader::{DocumentLoader, LoadType};
|
use crate::document_loader::{DocumentLoader, LoadType};
|
||||||
use crate::dom::attr::Attr;
|
use crate::dom::attr::Attr;
|
||||||
use crate::dom::beforeunloadevent::BeforeUnloadEvent;
|
use crate::dom::beforeunloadevent::BeforeUnloadEvent;
|
||||||
|
@ -384,6 +385,8 @@ pub struct Document {
|
||||||
/// A timeline for animations which is used for synchronizing animations.
|
/// A timeline for animations which is used for synchronizing animations.
|
||||||
/// https://drafts.csswg.org/web-animations/#timeline
|
/// https://drafts.csswg.org/web-animations/#timeline
|
||||||
animation_timeline: DomRefCell<AnimationTimeline>,
|
animation_timeline: DomRefCell<AnimationTimeline>,
|
||||||
|
/// Animations for this Document
|
||||||
|
animations: DomRefCell<Animations>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
@ -2913,6 +2916,7 @@ impl Document {
|
||||||
} else {
|
} else {
|
||||||
DomRefCell::new(AnimationTimeline::new())
|
DomRefCell::new(AnimationTimeline::new())
|
||||||
},
|
},
|
||||||
|
animations: DomRefCell::new(Animations::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3615,17 +3619,27 @@ impl Document {
|
||||||
.collect()
|
.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);
|
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();
|
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()
|
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 {
|
impl Element {
|
||||||
|
|
|
@ -81,7 +81,7 @@ use dom_struct::dom_struct;
|
||||||
use embedder_traits::{EmbedderMsg, EventLoopWaker, PromptDefinition, PromptOrigin, PromptResult};
|
use embedder_traits::{EmbedderMsg, EventLoopWaker, PromptDefinition, PromptOrigin, PromptResult};
|
||||||
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
|
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
|
||||||
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
|
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
|
||||||
use ipc_channel::ipc::{channel, IpcSender};
|
use ipc_channel::ipc::IpcSender;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use js::jsapi::Heap;
|
use js::jsapi::Heap;
|
||||||
use js::jsapi::JSAutoRealm;
|
use js::jsapi::JSAutoRealm;
|
||||||
|
@ -1305,9 +1305,9 @@ impl WindowMethods for Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn RunningAnimationCount(&self) -> u32 {
|
fn RunningAnimationCount(&self) -> u32 {
|
||||||
let (sender, receiver) = channel().unwrap();
|
self.document
|
||||||
let _ = self.layout_chan.send(Msg::GetRunningAnimations(sender));
|
.get()
|
||||||
receiver.recv().unwrap_or(0) as u32
|
.map_or(0, |d| d.animations().running_animation_count() as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-name
|
// https://html.spec.whatwg.org/multipage/#dom-name
|
||||||
|
@ -1643,6 +1643,7 @@ impl Window {
|
||||||
dom_count: document.dom_count(),
|
dom_count: document.dom_count(),
|
||||||
pending_restyles: document.drain_pending_restyles(),
|
pending_restyles: document.drain_pending_restyles(),
|
||||||
animation_timeline_value: document.current_animation_timeline_value(),
|
animation_timeline_value: document.current_animation_timeline_value(),
|
||||||
|
animations: document.animations().sets.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.layout_chan
|
self.layout_chan
|
||||||
|
@ -1706,8 +1707,9 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let update = document.update_animations();
|
||||||
unsafe {
|
unsafe {
|
||||||
ScriptThread::note_newly_animating_nodes(pipeline_id, complete.newly_animating_nodes);
|
ScriptThread::process_animations_update(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
|
@ -48,6 +48,7 @@ extern crate servo_atoms;
|
||||||
extern crate style;
|
extern crate style;
|
||||||
|
|
||||||
mod animation_timeline;
|
mod animation_timeline;
|
||||||
|
mod animations;
|
||||||
#[warn(deprecated)]
|
#[warn(deprecated)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod task;
|
mod task;
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
//! a page runs its course and the script thread returns to processing events in the main event
|
//! a page runs its course and the script thread returns to processing events in the main event
|
||||||
//! loop.
|
//! loop.
|
||||||
|
|
||||||
|
use crate::animations::{
|
||||||
|
AnimationsUpdate, TransitionOrAnimationEvent, TransitionOrAnimationEventType,
|
||||||
|
};
|
||||||
use crate::devtools;
|
use crate::devtools;
|
||||||
use crate::document_loader::DocumentLoader;
|
use crate::document_loader::DocumentLoader;
|
||||||
use crate::dom::animationevent::AnimationEvent;
|
use crate::dom::animationevent::AnimationEvent;
|
||||||
|
@ -141,8 +144,8 @@ use script_traits::{
|
||||||
LayoutMsg, LoadData, LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType,
|
LayoutMsg, LoadData, LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType,
|
||||||
NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory,
|
NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory,
|
||||||
ScriptToConstellationChan, StructuredSerializedData, TimerSchedulerMsg, TouchEventType,
|
ScriptToConstellationChan, StructuredSerializedData, TimerSchedulerMsg, TouchEventType,
|
||||||
TouchId, TransitionOrAnimationEvent, TransitionOrAnimationEventType, UntrustedNodeAddress,
|
TouchId, UntrustedNodeAddress, UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta,
|
||||||
UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta, WindowSizeData, WindowSizeType,
|
WindowSizeData, WindowSizeType,
|
||||||
};
|
};
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_config::opts;
|
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
|
// thread during parsing. For this reason, we don't trace the
|
||||||
// incomplete parser contexts during GC.
|
// incomplete parser contexts during GC.
|
||||||
type IncompleteParserContexts = Vec<(PipelineId, ParserContext)>;
|
type IncompleteParserContexts = Vec<(PipelineId, ParserContext)>;
|
||||||
unsafe_no_jsmanaged_fields!(RefCell<IncompleteParserContexts>);
|
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(TaskQueue<MainThreadScriptMsg>);
|
unsafe_no_jsmanaged_fields!(TaskQueue<MainThreadScriptMsg>);
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(dyn BackgroundHangMonitorRegister);
|
unsafe_no_jsmanaged_fields!(dyn BackgroundHangMonitorRegister);
|
||||||
unsafe_no_jsmanaged_fields!(dyn BackgroundHangMonitor);
|
unsafe_no_jsmanaged_fields!(dyn BackgroundHangMonitor);
|
||||||
|
|
||||||
|
@ -644,6 +645,9 @@ pub struct ScriptThread {
|
||||||
/// of the transition.
|
/// of the transition.
|
||||||
animating_nodes: DomRefCell<HashMap<PipelineId, Vec<Dom<Node>>>>,
|
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>
|
/// <https://html.spec.whatwg.org/multipage/#custom-element-reactions-stack>
|
||||||
custom_element_reaction_stack: CustomElementReactionStack,
|
custom_element_reaction_stack: CustomElementReactionStack,
|
||||||
|
|
||||||
|
@ -826,20 +830,35 @@ impl ScriptThread {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn note_newly_animating_nodes(
|
/// Consume the list of pointer addresses corresponding to DOM nodes that are animating
|
||||||
pipeline_id: PipelineId,
|
/// and root them in a per-pipeline list of nodes.
|
||||||
nodes: Vec<UntrustedNodeAddress>,
|
///
|
||||||
) {
|
/// 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| {
|
SCRIPT_THREAD_ROOT.with(|root| {
|
||||||
let script_thread = &*root.get().unwrap();
|
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 js_runtime = script_thread.js_runtime.rt();
|
||||||
let new_nodes = nodes
|
let new_nodes = update
|
||||||
|
.newly_animating_nodes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|n| Dom::from_ref(&*from_untrusted_node_address(js_runtime, n)));
|
.map(|n| Dom::from_ref(&*from_untrusted_node_address(js_runtime, n)));
|
||||||
script_thread
|
script_thread
|
||||||
.animating_nodes
|
.animating_nodes
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.entry(pipeline_id)
|
.entry(update.pipeline_id)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.extend(new_nodes);
|
.extend(new_nodes);
|
||||||
})
|
})
|
||||||
|
@ -1354,6 +1373,7 @@ impl ScriptThread {
|
||||||
docs_with_no_blocking_loads: Default::default(),
|
docs_with_no_blocking_loads: Default::default(),
|
||||||
|
|
||||||
animating_nodes: Default::default(),
|
animating_nodes: Default::default(),
|
||||||
|
animation_events: Default::default(),
|
||||||
|
|
||||||
custom_element_reaction_stack: CustomElementReactionStack::new(),
|
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.
|
// 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
|
// TODO(mrobinson): This should also update the current animations to conform to
|
||||||
// to conform to the HTML specification. This might mean having events for rooting
|
// the HTML specification.
|
||||||
// DOM nodes and ultimately all animations living in script.
|
|
||||||
fn update_animations_and_send_events(&self) {
|
fn update_animations_and_send_events(&self) {
|
||||||
for (_, document) in self.documents.borrow().iter() {
|
for (_, document) in self.documents.borrow().iter() {
|
||||||
// Only update the time if it isn't being managed by a test.
|
// Only update the time if it isn't being managed by a test.
|
||||||
|
@ -1630,6 +1649,13 @@ impl ScriptThread {
|
||||||
document.update_animation_timeline();
|
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 {
|
fn categorize_msg(&self, msg: &MixedMessage) -> ScriptThreadEventCategory {
|
||||||
|
@ -1720,7 +1746,6 @@ impl ScriptThread {
|
||||||
FocusIFrame(id, ..) => Some(id),
|
FocusIFrame(id, ..) => Some(id),
|
||||||
WebDriverScriptCommand(id, ..) => Some(id),
|
WebDriverScriptCommand(id, ..) => Some(id),
|
||||||
TickAllAnimations(id, ..) => Some(id),
|
TickAllAnimations(id, ..) => Some(id),
|
||||||
TransitionOrAnimationEvent { .. } => None,
|
|
||||||
WebFontLoaded(id) => Some(id),
|
WebFontLoaded(id) => Some(id),
|
||||||
DispatchIFrameLoadEvent {
|
DispatchIFrameLoadEvent {
|
||||||
target: _,
|
target: _,
|
||||||
|
@ -1921,9 +1946,6 @@ impl ScriptThread {
|
||||||
ConstellationControlMsg::TickAllAnimations(pipeline_id, tick_type) => {
|
ConstellationControlMsg::TickAllAnimations(pipeline_id, tick_type) => {
|
||||||
self.handle_tick_all_animations(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) => {
|
ConstellationControlMsg::WebFontLoaded(pipeline_id) => {
|
||||||
self.handle_web_font_loaded(pipeline_id)
|
self.handle_web_font_loaded(pipeline_id)
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,6 +16,7 @@ atomic_refcell = "0.1"
|
||||||
canvas_traits = {path = "../canvas_traits"}
|
canvas_traits = {path = "../canvas_traits"}
|
||||||
crossbeam-channel = "0.4"
|
crossbeam-channel = "0.4"
|
||||||
euclid = "0.20"
|
euclid = "0.20"
|
||||||
|
fxhash = "0.2"
|
||||||
gfx_traits = {path = "../gfx_traits"}
|
gfx_traits = {path = "../gfx_traits"}
|
||||||
html5ever = "0.25"
|
html5ever = "0.25"
|
||||||
ipc-channel = "0.14"
|
ipc-channel = "0.14"
|
||||||
|
@ -25,6 +26,7 @@ malloc_size_of_derive = "0.1"
|
||||||
metrics = {path = "../metrics"}
|
metrics = {path = "../metrics"}
|
||||||
msg = {path = "../msg"}
|
msg = {path = "../msg"}
|
||||||
net_traits = {path = "../net_traits"}
|
net_traits = {path = "../net_traits"}
|
||||||
|
parking_lot = "0.9"
|
||||||
profile_traits = {path = "../profile_traits"}
|
profile_traits = {path = "../profile_traits"}
|
||||||
range = {path = "../range"}
|
range = {path = "../range"}
|
||||||
script_traits = {path = "../script_traits"}
|
script_traits = {path = "../script_traits"}
|
||||||
|
|
|
@ -7,20 +7,25 @@ use crate::{PendingImage, TrustedNodeAddress};
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
use euclid::default::{Point2D, Rect};
|
use euclid::default::{Point2D, Rect};
|
||||||
|
use fxhash::FxHashMap;
|
||||||
use gfx_traits::Epoch;
|
use gfx_traits::Epoch;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
use metrics::PaintTimeMetrics;
|
use metrics::PaintTimeMetrics;
|
||||||
use msg::constellation_msg::{BackgroundHangMonitorRegister, BrowsingContextId, PipelineId};
|
use msg::constellation_msg::{BackgroundHangMonitorRegister, BrowsingContextId, PipelineId};
|
||||||
use net_traits::image_cache::ImageCache;
|
use net_traits::image_cache::ImageCache;
|
||||||
|
use parking_lot::RwLock;
|
||||||
use profile_traits::mem::ReportsChan;
|
use profile_traits::mem::ReportsChan;
|
||||||
use script_traits::Painter;
|
use script_traits::Painter;
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
|
use script_traits::{
|
||||||
use script_traits::{ScrollState, UntrustedNodeAddress, WindowSizeData};
|
ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, ScrollState,
|
||||||
|
WindowSizeData,
|
||||||
|
};
|
||||||
use servo_arc::Arc as ServoArc;
|
use servo_arc::Arc as ServoArc;
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use style::animation::ElementAnimationSet;
|
||||||
use style::context::QuirksMode;
|
use style::context::QuirksMode;
|
||||||
use style::dom::OpaqueNode;
|
use style::dom::OpaqueNode;
|
||||||
use style::invalidation::element::restyle_hints::RestyleHint;
|
use style::invalidation::element::restyle_hints::RestyleHint;
|
||||||
|
@ -87,9 +92,6 @@ pub enum Msg {
|
||||||
|
|
||||||
/// Send to layout the precise time when the navigation started.
|
/// Send to layout the precise time when the navigation started.
|
||||||
SetNavigationStart(u64),
|
SetNavigationStart(u64),
|
||||||
|
|
||||||
/// Request the current number of animations that are running.
|
|
||||||
GetRunningAnimations(IpcSender<usize>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -183,8 +185,6 @@ pub struct Reflow {
|
||||||
pub struct ReflowComplete {
|
pub struct ReflowComplete {
|
||||||
/// The list of images that were encountered that are in progress.
|
/// The list of images that were encountered that are in progress.
|
||||||
pub pending_images: Vec<PendingImage>,
|
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.
|
/// Information needed for a script-initiated reflow.
|
||||||
|
@ -209,6 +209,8 @@ pub struct ScriptReflow {
|
||||||
pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
|
pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
|
||||||
/// The current animation timeline value.
|
/// The current animation timeline value.
|
||||||
pub animation_timeline_value: f64,
|
pub animation_timeline_value: f64,
|
||||||
|
/// The set of animations for this document.
|
||||||
|
pub animations: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LayoutThreadInit {
|
pub struct LayoutThreadInit {
|
||||||
|
|
|
@ -282,58 +282,6 @@ pub enum UpdatePipelineIdReason {
|
||||||
Traversal,
|
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.
|
/// Messages sent from the constellation or layout to the script thread.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub enum ConstellationControlMsg {
|
pub enum ConstellationControlMsg {
|
||||||
|
@ -420,8 +368,6 @@ pub enum ConstellationControlMsg {
|
||||||
WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
|
WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
|
||||||
/// Notifies script thread that all animations are done
|
/// Notifies script thread that all animations are done
|
||||||
TickAllAnimations(PipelineId, AnimationTickType),
|
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
|
/// Notifies the script thread that a new Web font has been loaded, and thus the page should be
|
||||||
/// reflowed.
|
/// reflowed.
|
||||||
WebFontLoaded(PipelineId),
|
WebFontLoaded(PipelineId),
|
||||||
|
@ -481,7 +427,6 @@ impl fmt::Debug for ConstellationControlMsg {
|
||||||
FocusIFrame(..) => "FocusIFrame",
|
FocusIFrame(..) => "FocusIFrame",
|
||||||
WebDriverScriptCommand(..) => "WebDriverScriptCommand",
|
WebDriverScriptCommand(..) => "WebDriverScriptCommand",
|
||||||
TickAllAnimations(..) => "TickAllAnimations",
|
TickAllAnimations(..) => "TickAllAnimations",
|
||||||
TransitionOrAnimationEvent { .. } => "TransitionOrAnimationEvent",
|
|
||||||
WebFontLoaded(..) => "WebFontLoaded",
|
WebFontLoaded(..) => "WebFontLoaded",
|
||||||
DispatchIFrameLoadEvent { .. } => "DispatchIFrameLoadEvent",
|
DispatchIFrameLoadEvent { .. } => "DispatchIFrameLoadEvent",
|
||||||
DispatchStorageEvent(..) => "DispatchStorageEvent",
|
DispatchStorageEvent(..) => "DispatchStorageEvent",
|
||||||
|
|
|
@ -61,8 +61,6 @@ pub struct IFrameSizeMsg {
|
||||||
/// Messages from the layout to the constellation.
|
/// Messages from the layout to the constellation.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub enum LayoutMsg {
|
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.
|
/// Inform the constellation of the size of the iframe's viewport.
|
||||||
IFrameSizes(Vec<IFrameSizeMsg>),
|
IFrameSizes(Vec<IFrameSizeMsg>),
|
||||||
/// Requests that the constellation inform the compositor that it needs to record
|
/// 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 {
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use self::LayoutMsg::*;
|
use self::LayoutMsg::*;
|
||||||
let variant = match *self {
|
let variant = match *self {
|
||||||
ChangeRunningAnimationsState(..) => "ChangeRunningAnimationsState",
|
|
||||||
IFrameSizes(..) => "IFrameSizes",
|
IFrameSizes(..) => "IFrameSizes",
|
||||||
PendingPaintMetric(..) => "PendingPaintMetric",
|
PendingPaintMetric(..) => "PendingPaintMetric",
|
||||||
ViewportConstrained(..) => "ViewportConstrained",
|
ViewportConstrained(..) => "ViewportConstrained",
|
||||||
|
|
|
@ -27,7 +27,7 @@ use servo_arc::Arc;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// Represents an animation for a given property.
|
/// Represents an animation for a given property.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, MallocSizeOf)]
|
||||||
pub struct PropertyAnimation {
|
pub struct PropertyAnimation {
|
||||||
/// An `AnimatedProperty` that this `PropertyAnimation` corresponds to.
|
/// An `AnimatedProperty` that this `PropertyAnimation` corresponds to.
|
||||||
property: AnimatedProperty,
|
property: AnimatedProperty,
|
||||||
|
@ -136,7 +136,7 @@ impl PropertyAnimation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This structure represents the state of an animation.
|
/// This structure represents the state of an animation.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
||||||
pub enum AnimationState {
|
pub enum AnimationState {
|
||||||
/// This animation is paused. The inner field is the percentage of progress
|
/// This animation is paused. The inner field is the percentage of progress
|
||||||
/// when it was paused, from 0 to 1.
|
/// 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
|
/// 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.
|
/// have to keep track the current iteration and the max iteration count.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, MallocSizeOf)]
|
||||||
pub enum KeyframesIterationState {
|
pub enum KeyframesIterationState {
|
||||||
/// Infinite iterations, so no need to track a state.
|
/// Infinite iterations, so no need to track a state.
|
||||||
Infinite,
|
Infinite,
|
||||||
|
@ -164,7 +164,7 @@ pub enum KeyframesIterationState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A CSS Animation
|
/// A CSS Animation
|
||||||
#[derive(Clone)]
|
#[derive(Clone, MallocSizeOf)]
|
||||||
pub struct Animation {
|
pub struct Animation {
|
||||||
/// The node associated with this animation.
|
/// The node associated with this animation.
|
||||||
pub node: OpaqueNode,
|
pub node: OpaqueNode,
|
||||||
|
@ -198,6 +198,7 @@ pub struct Animation {
|
||||||
|
|
||||||
/// The original cascade style, needed to compute the generated keyframes of
|
/// The original cascade style, needed to compute the generated keyframes of
|
||||||
/// the animation.
|
/// the animation.
|
||||||
|
#[ignore_malloc_size_of = "ComputedValues"]
|
||||||
pub cascade_style: Arc<ComputedValues>,
|
pub cascade_style: Arc<ComputedValues>,
|
||||||
|
|
||||||
/// Whether or not this animation is new and or has already been tracked
|
/// 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
|
/// A CSS Transition
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, MallocSizeOf)]
|
||||||
pub struct Transition {
|
pub struct Transition {
|
||||||
/// The node associated with this animation.
|
/// The node associated with this animation.
|
||||||
pub node: OpaqueNode,
|
pub node: OpaqueNode,
|
||||||
|
@ -597,7 +598,7 @@ impl Transition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds the animation state for a particular element.
|
/// Holds the animation state for a particular element.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, MallocSizeOf)]
|
||||||
pub struct ElementAnimationSet {
|
pub struct ElementAnimationSet {
|
||||||
/// The animations for this element.
|
/// The animations for this element.
|
||||||
pub animations: Vec<Animation>,
|
pub animations: Vec<Animation>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue