mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Auto merge of #26285 - mrobinson:animation-animation-state, r=jdm,emilio
Add ElementAnimationState and PossibleElementAnimationState This refactor is preparation for implementing a specification compliant transitions and animations processing model. These data structures hold all the animation information about a single node. Since adding, updating, and modifying animations for a single node are all interdependent, it makes sense to start encapsulating animation data and functionality into a single data structure. This also opens up the possibility for easier concurrency in the future by more easily allowing per-node mutexes. <!-- 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 - [ ] These changes fix #___ (GitHub issue number if applicable) <!-- Either: --> - [ ] There are tests for these changes OR - [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
0540c4a284
9 changed files with 332 additions and 454 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -5422,7 +5422,6 @@ dependencies = [
|
|||
"bindgen",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"crossbeam-channel",
|
||||
"cssparser",
|
||||
"derive_more",
|
||||
"encoding_rs",
|
||||
|
|
|
@ -8,7 +8,6 @@ use crate::context::LayoutContext;
|
|||
use crate::display_list::items::OpaqueNode;
|
||||
use crate::flow::{Flow, GetBaseFlow};
|
||||
use crate::opaque_node::OpaqueNodeMethods;
|
||||
use crossbeam_channel::Receiver;
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
|
@ -16,95 +15,106 @@ use script_traits::UntrustedNodeAddress;
|
|||
use script_traits::{
|
||||
AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg, TransitionEventType,
|
||||
};
|
||||
use style::animation::{update_style_for_animation, Animation};
|
||||
use style::animation::{update_style_for_animation, Animation, ElementAnimationState};
|
||||
use style::dom::TElement;
|
||||
use style::font_metrics::ServoMetricsProvider;
|
||||
use style::selector_parser::RestyleDamage;
|
||||
use style::timer::Timer;
|
||||
|
||||
/// Processes any new animations that were discovered after style recalculation.
|
||||
/// Also expire any old animations that have completed, inserting them into
|
||||
/// `expired_animations`.
|
||||
pub fn update_animation_state<E>(
|
||||
/// Processes any new animations that were discovered after style recalculation. Also
|
||||
/// finish any animations that have completed, inserting them into `finished_animations`.
|
||||
pub fn update_animation_states<E>(
|
||||
constellation_chan: &IpcSender<ConstellationMsg>,
|
||||
script_chan: &IpcSender<ConstellationControlMsg>,
|
||||
running_animations: &mut FxHashMap<OpaqueNode, Vec<Animation>>,
|
||||
expired_animations: &mut FxHashMap<OpaqueNode, Vec<Animation>>,
|
||||
cancelled_animations: &mut FxHashMap<OpaqueNode, Vec<Animation>>,
|
||||
mut keys_to_remove: FxHashSet<OpaqueNode>,
|
||||
animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationState>,
|
||||
invalid_nodes: FxHashSet<OpaqueNode>,
|
||||
mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>,
|
||||
new_animations_receiver: &Receiver<Animation>,
|
||||
pipeline_id: PipelineId,
|
||||
timer: &Timer,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
send_events_for_cancelled_animations(script_chan, cancelled_animations, pipeline_id);
|
||||
let had_running_animations = animation_states
|
||||
.values()
|
||||
.any(|state| !state.running_animations.is_empty());
|
||||
|
||||
let mut new_running_animations = vec![];
|
||||
while let Ok(animation) = new_animations_receiver.try_recv() {
|
||||
let mut should_push = true;
|
||||
if let Animation::Keyframes(ref node, _, ref name, ref state) = animation {
|
||||
// If the animation was already present in the list for the
|
||||
// node, just update its state, else push the new animation to
|
||||
// run.
|
||||
if let Some(ref mut animations) = running_animations.get_mut(node) {
|
||||
// TODO: This being linear is probably not optimal.
|
||||
for anim in animations.iter_mut() {
|
||||
if let Animation::Keyframes(_, _, ref anim_name, ref mut anim_state) = *anim {
|
||||
if *name == *anim_name {
|
||||
debug!("update_animation_state: Found other animation {}", name);
|
||||
anim_state.update_from_other(&state, timer);
|
||||
should_push = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for node in &invalid_nodes {
|
||||
if let Some(mut state) = animation_states.remove(node) {
|
||||
state.cancel_all_animations();
|
||||
send_events_for_cancelled_animations(script_chan, &mut state, pipeline_id);
|
||||
}
|
||||
|
||||
if should_push {
|
||||
new_running_animations.push(animation);
|
||||
}
|
||||
}
|
||||
|
||||
if running_animations.is_empty() && new_running_animations.is_empty() {
|
||||
// Nothing to do. Return early so we don't flood the compositor with
|
||||
// `ChangeRunningAnimationsState` messages.
|
||||
return;
|
||||
}
|
||||
|
||||
let now = timer.seconds();
|
||||
// Expire old running animations.
|
||||
//
|
||||
// TODO: Do not expunge Keyframes animations, since we need that state if
|
||||
// the animation gets re-triggered. Probably worth splitting in two
|
||||
// different maps, or at least using a linked list?
|
||||
for (key, running_animations) in running_animations.iter_mut() {
|
||||
let mut animations_still_running = vec![];
|
||||
for mut running_animation in running_animations.drain(..) {
|
||||
let still_running = !running_animation.is_expired() &&
|
||||
match running_animation {
|
||||
Animation::Transition(_, started_at, ref property_animation) => {
|
||||
now < started_at + (property_animation.duration)
|
||||
},
|
||||
Animation::Keyframes(_, _, _, ref mut state) => {
|
||||
// This animation is still running, or we need to keep
|
||||
// iterating.
|
||||
now < state.started_at + state.duration || state.tick()
|
||||
},
|
||||
};
|
||||
|
||||
debug!(
|
||||
"update_animation_state({:?}): {:?}",
|
||||
still_running, running_animation
|
||||
);
|
||||
|
||||
if still_running {
|
||||
animations_still_running.push(running_animation);
|
||||
continue;
|
||||
let mut have_running_animations = false;
|
||||
for (node, animation_state) in animation_states.iter_mut() {
|
||||
// TODO(mrobinson): This should really be triggering transitionrun messages
|
||||
// on the script thread.
|
||||
if let Some(ref mut newly_transitioning_nodes) = newly_transitioning_nodes {
|
||||
let number_of_new_transitions = animation_state
|
||||
.new_animations
|
||||
.iter()
|
||||
.filter(|animation| animation.is_transition())
|
||||
.count();
|
||||
for _ in 0..number_of_new_transitions {
|
||||
newly_transitioning_nodes.push(node.to_untrusted_node_address());
|
||||
}
|
||||
}
|
||||
|
||||
update_animation_state::<E>(script_chan, animation_state, pipeline_id, now);
|
||||
|
||||
have_running_animations =
|
||||
have_running_animations || !animation_state.running_animations.is_empty();
|
||||
}
|
||||
|
||||
// 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 present = match (had_running_animations, have_running_animations) {
|
||||
(true, false) => AnimationState::NoAnimationsPresent,
|
||||
(false, true) => AnimationState::AnimationsPresent,
|
||||
_ => return,
|
||||
};
|
||||
constellation_chan
|
||||
.send(ConstellationMsg::ChangeRunningAnimationsState(
|
||||
pipeline_id,
|
||||
present,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn update_animation_state<E>(
|
||||
script_chan: &IpcSender<ConstellationControlMsg>,
|
||||
animation_state: &mut ElementAnimationState,
|
||||
pipeline_id: PipelineId,
|
||||
now: f64,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
send_events_for_cancelled_animations(script_chan, animation_state, pipeline_id);
|
||||
|
||||
let mut running_animations =
|
||||
std::mem::replace(&mut animation_state.running_animations, Vec::new());
|
||||
for mut running_animation in running_animations.drain(..) {
|
||||
let still_running = !running_animation.is_expired() &&
|
||||
match running_animation {
|
||||
Animation::Transition(_, started_at, ref property_animation) => {
|
||||
now < started_at + (property_animation.duration)
|
||||
},
|
||||
Animation::Keyframes(_, _, _, ref mut state) => {
|
||||
// This animation is still running, or we need to keep
|
||||
// iterating.
|
||||
now < state.started_at + state.duration || state.tick()
|
||||
},
|
||||
};
|
||||
|
||||
// If the animation is still running, add it back to the list of running animations.
|
||||
if still_running {
|
||||
animation_state.running_animations.push(running_animation);
|
||||
} else {
|
||||
debug!("Finishing transition: {:?}", running_animation);
|
||||
if let Animation::Transition(node, _, ref property_animation) = running_animation {
|
||||
script_chan
|
||||
.send(ConstellationControlMsg::TransitionEvent {
|
||||
|
@ -116,56 +126,13 @@ pub fn update_animation_state<E>(
|
|||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
debug!("expiring animation for {:?}", running_animation);
|
||||
expired_animations
|
||||
.entry(*key)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(running_animation);
|
||||
}
|
||||
|
||||
if animations_still_running.is_empty() {
|
||||
keys_to_remove.insert(*key);
|
||||
} else {
|
||||
*running_animations = animations_still_running
|
||||
animation_state.finished_animations.push(running_animation);
|
||||
}
|
||||
}
|
||||
|
||||
for key in keys_to_remove {
|
||||
running_animations.remove(&key).unwrap();
|
||||
}
|
||||
|
||||
// Add new running animations.
|
||||
for new_running_animation in new_running_animations {
|
||||
if new_running_animation.is_transition() {
|
||||
match newly_transitioning_nodes {
|
||||
Some(ref mut nodes) => {
|
||||
nodes.push(new_running_animation.node().to_untrusted_node_address());
|
||||
},
|
||||
None => {
|
||||
warn!("New transition encountered from compositor-initiated layout.");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
running_animations
|
||||
.entry(*new_running_animation.node())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(new_running_animation)
|
||||
}
|
||||
|
||||
let animation_state = if running_animations.is_empty() {
|
||||
AnimationState::NoAnimationsPresent
|
||||
} else {
|
||||
AnimationState::AnimationsPresent
|
||||
};
|
||||
|
||||
constellation_chan
|
||||
.send(ConstellationMsg::ChangeRunningAnimationsState(
|
||||
pipeline_id,
|
||||
animation_state,
|
||||
))
|
||||
.unwrap();
|
||||
animation_state
|
||||
.running_animations
|
||||
.append(&mut animation_state.new_animations);
|
||||
}
|
||||
|
||||
/// Send events for cancelled animations. Currently this only handles cancelled
|
||||
|
@ -173,28 +140,25 @@ pub fn update_animation_state<E>(
|
|||
/// well.
|
||||
pub fn send_events_for_cancelled_animations(
|
||||
script_channel: &IpcSender<ConstellationControlMsg>,
|
||||
cancelled_animations: &mut FxHashMap<OpaqueNode, Vec<Animation>>,
|
||||
animation_state: &mut ElementAnimationState,
|
||||
pipeline_id: PipelineId,
|
||||
) {
|
||||
for (node, animations) in cancelled_animations.drain() {
|
||||
for animation in animations {
|
||||
match animation {
|
||||
Animation::Transition(transition_node, _, ref property_animation) => {
|
||||
debug_assert!(transition_node == node);
|
||||
script_channel
|
||||
.send(ConstellationControlMsg::TransitionEvent {
|
||||
pipeline_id,
|
||||
event_type: TransitionEventType::TransitionCancel,
|
||||
node: node.to_untrusted_node_address(),
|
||||
property_name: property_animation.property_name().into(),
|
||||
elapsed_time: property_animation.duration,
|
||||
})
|
||||
.unwrap();
|
||||
},
|
||||
Animation::Keyframes(..) => {
|
||||
warn!("Got unexpected animation in expired transitions list.")
|
||||
},
|
||||
}
|
||||
for animation in animation_state.cancelled_animations.drain(..) {
|
||||
match animation {
|
||||
Animation::Transition(node, _, ref property_animation) => {
|
||||
script_channel
|
||||
.send(ConstellationControlMsg::TransitionEvent {
|
||||
pipeline_id,
|
||||
event_type: TransitionEventType::TransitionCancel,
|
||||
node: node.to_untrusted_node_address(),
|
||||
property_name: property_animation.property_name().into(),
|
||||
elapsed_time: property_animation.duration,
|
||||
})
|
||||
.unwrap();
|
||||
},
|
||||
Animation::Keyframes(..) => {
|
||||
warn!("Got unexpected animation in finished transitions list.")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,46 +169,48 @@ pub fn send_events_for_cancelled_animations(
|
|||
pub fn recalc_style_for_animations<E>(
|
||||
context: &LayoutContext,
|
||||
flow: &mut dyn Flow,
|
||||
animations: &FxHashMap<OpaqueNode, Vec<Animation>>,
|
||||
animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>,
|
||||
) -> FxHashSet<OpaqueNode>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
let mut invalid_nodes = animations.keys().cloned().collect();
|
||||
do_recalc_style_for_animations::<E>(context, flow, animations, &mut invalid_nodes);
|
||||
let mut invalid_nodes = animation_states.keys().cloned().collect();
|
||||
do_recalc_style_for_animations::<E>(context, flow, animation_states, &mut invalid_nodes);
|
||||
invalid_nodes
|
||||
}
|
||||
|
||||
fn do_recalc_style_for_animations<E>(
|
||||
context: &LayoutContext,
|
||||
flow: &mut dyn Flow,
|
||||
animations: &FxHashMap<OpaqueNode, Vec<Animation>>,
|
||||
animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>,
|
||||
invalid_nodes: &mut FxHashSet<OpaqueNode>,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
let mut damage = RestyleDamage::empty();
|
||||
flow.mutate_fragments(&mut |fragment| {
|
||||
if let Some(ref animations) = animations.get(&fragment.node) {
|
||||
invalid_nodes.remove(&fragment.node);
|
||||
for animation in animations.iter() {
|
||||
let old_style = fragment.style.clone();
|
||||
update_style_for_animation::<E>(
|
||||
&context.style_context,
|
||||
animation,
|
||||
&mut fragment.style,
|
||||
&ServoMetricsProvider,
|
||||
);
|
||||
let difference =
|
||||
RestyleDamage::compute_style_difference(&old_style, &fragment.style);
|
||||
damage |= difference.damage;
|
||||
}
|
||||
let animations = match animation_states.get(&fragment.node) {
|
||||
Some(state) => &state.running_animations,
|
||||
None => return,
|
||||
};
|
||||
|
||||
invalid_nodes.remove(&fragment.node);
|
||||
for animation in animations.iter() {
|
||||
let old_style = fragment.style.clone();
|
||||
update_style_for_animation::<E>(
|
||||
&context.style_context,
|
||||
animation,
|
||||
&mut fragment.style,
|
||||
&ServoMetricsProvider,
|
||||
);
|
||||
let difference = RestyleDamage::compute_style_difference(&old_style, &fragment.style);
|
||||
damage |= difference.damage;
|
||||
}
|
||||
});
|
||||
|
||||
let base = flow.mut_base();
|
||||
base.restyle_damage.insert(damage);
|
||||
for kid in base.children.iter_mut() {
|
||||
do_recalc_style_for_animations::<E>(context, kid, animations, invalid_nodes)
|
||||
do_recalc_style_for_animations::<E>(context, kid, animation_states, invalid_nodes)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ mod dom_wrapper;
|
|||
|
||||
use crate::dom_wrapper::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode};
|
||||
use app_units::Au;
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use embedder_traits::resources::{self, Resource};
|
||||
use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D};
|
||||
use fnv::FnvHashMap;
|
||||
|
@ -99,9 +99,9 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use style::animation::Animation;
|
||||
use style::animation::ElementAnimationState;
|
||||
use style::context::SharedStyleContext;
|
||||
use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters};
|
||||
use style::context::{SharedStyleContext, ThreadLocalStyleContextCreationInfo};
|
||||
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TDocument, TElement, TNode};
|
||||
use style::driver;
|
||||
use style::error_reporting::RustLogReporter;
|
||||
|
@ -185,13 +185,6 @@ pub struct LayoutThread {
|
|||
/// This can be used to easily check for invalid stale data.
|
||||
generation: Cell<u32>,
|
||||
|
||||
/// A channel on which new animations that have been triggered by style recalculation can be
|
||||
/// sent.
|
||||
new_animations_sender: Sender<Animation>,
|
||||
|
||||
/// Receives newly-discovered animations.
|
||||
new_animations_receiver: Receiver<Animation>,
|
||||
|
||||
/// The number of Web fonts that have been requested but not yet loaded.
|
||||
outstanding_web_fonts: Arc<AtomicUsize>,
|
||||
|
||||
|
@ -201,14 +194,8 @@ pub struct LayoutThread {
|
|||
/// The document-specific shared lock used for author-origin stylesheets
|
||||
document_shared_lock: Option<SharedRwLock>,
|
||||
|
||||
/// The list of currently-running animations.
|
||||
running_animations: ServoArc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
|
||||
/// The list of animations that have expired since the last style recalculation.
|
||||
expired_animations: ServoArc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
|
||||
/// The list of animations that have been cancelled during the last style recalculation.
|
||||
cancelled_animations: ServoArc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
/// The animation state for all of our nodes.
|
||||
animation_states: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationState>>>,
|
||||
|
||||
/// A counter for epoch messages
|
||||
epoch: Cell<Epoch>,
|
||||
|
@ -541,9 +528,6 @@ impl LayoutThread {
|
|||
window_size.device_pixel_ratio,
|
||||
);
|
||||
|
||||
// Create the channel on which new animations can be sent.
|
||||
let (new_animations_sender, new_animations_receiver) = unbounded();
|
||||
|
||||
// Proxy IPC messages from the pipeline to the layout thread.
|
||||
let pipeline_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(pipeline_port);
|
||||
|
||||
|
@ -572,14 +556,10 @@ impl LayoutThread {
|
|||
font_cache_sender: ipc_font_cache_sender,
|
||||
parallel_flag: true,
|
||||
generation: Cell::new(0),
|
||||
new_animations_sender: new_animations_sender,
|
||||
new_animations_receiver: new_animations_receiver,
|
||||
outstanding_web_fonts: Arc::new(AtomicUsize::new(0)),
|
||||
root_flow: RefCell::new(None),
|
||||
document_shared_lock: None,
|
||||
running_animations: ServoArc::new(RwLock::new(Default::default())),
|
||||
expired_animations: ServoArc::new(RwLock::new(Default::default())),
|
||||
cancelled_animations: ServoArc::new(RwLock::new(Default::default())),
|
||||
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)),
|
||||
|
@ -646,9 +626,6 @@ impl LayoutThread {
|
|||
snapshot_map: &'a SnapshotMap,
|
||||
origin: ImmutableOrigin,
|
||||
) -> LayoutContext<'a> {
|
||||
let thread_local_style_context_creation_data =
|
||||
ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone());
|
||||
|
||||
LayoutContext {
|
||||
id: self.id,
|
||||
origin,
|
||||
|
@ -657,11 +634,8 @@ impl LayoutThread {
|
|||
options: GLOBAL_STYLE_DATA.options.clone(),
|
||||
guards,
|
||||
visited_styles_enabled: false,
|
||||
running_animations: self.running_animations.clone(),
|
||||
expired_animations: self.expired_animations.clone(),
|
||||
cancelled_animations: self.cancelled_animations.clone(),
|
||||
animation_states: self.animation_states.clone(),
|
||||
registered_speculative_painters: &self.registered_painters,
|
||||
local_context_creation_data: Mutex::new(thread_local_style_context_creation_data),
|
||||
timer: self.timer.clone(),
|
||||
traversal_flags: TraversalFlags::empty(),
|
||||
snapshot_map: snapshot_map,
|
||||
|
@ -882,7 +856,13 @@ impl LayoutThread {
|
|||
self.paint_time_metrics.set_navigation_start(time);
|
||||
},
|
||||
Msg::GetRunningAnimations(sender) => {
|
||||
let _ = sender.send(self.running_animations.read().len());
|
||||
let running_animation_count = self
|
||||
.animation_states
|
||||
.read()
|
||||
.values()
|
||||
.map(|state| state.running_animations.len())
|
||||
.sum();
|
||||
let _ = sender.send(running_animation_count);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1737,7 +1717,7 @@ impl LayoutThread {
|
|||
|
||||
let invalid_nodes = {
|
||||
// Perform an abbreviated style recalc that operates without access to the DOM.
|
||||
let animations = self.running_animations.read();
|
||||
let animation_states = self.animation_states.read();
|
||||
profile(
|
||||
profile_time::ProfilerCategory::LayoutStyleRecalc,
|
||||
self.profiler_metadata(),
|
||||
|
@ -1746,7 +1726,7 @@ impl LayoutThread {
|
|||
animation::recalc_style_for_animations::<ServoLayoutElement>(
|
||||
&layout_context,
|
||||
FlowRef::deref_mut(&mut root_flow),
|
||||
&animations,
|
||||
&animation_states,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
@ -1783,15 +1763,12 @@ impl LayoutThread {
|
|||
let newly_transitioning_nodes =
|
||||
newly_transitioning_nodes.as_mut().map(|nodes| &mut **nodes);
|
||||
// Kick off animations if any were triggered, expire completed ones.
|
||||
animation::update_animation_state::<ServoLayoutElement>(
|
||||
animation::update_animation_states::<ServoLayoutElement>(
|
||||
&self.constellation_chan,
|
||||
&self.script_chan,
|
||||
&mut *self.running_animations.write(),
|
||||
&mut *self.expired_animations.write(),
|
||||
&mut *self.cancelled_animations.write(),
|
||||
&mut *self.animation_states.write(),
|
||||
invalid_nodes,
|
||||
newly_transitioning_nodes,
|
||||
&self.new_animations_receiver,
|
||||
self.id,
|
||||
&self.timer,
|
||||
);
|
||||
|
|
|
@ -23,7 +23,7 @@ mod dom_wrapper;
|
|||
|
||||
use crate::dom_wrapper::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode};
|
||||
use app_units::Au;
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use embedder_traits::resources::{self, Resource};
|
||||
use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D};
|
||||
use fnv::FnvHashMap;
|
||||
|
@ -81,9 +81,9 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use style::animation::Animation;
|
||||
use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters};
|
||||
use style::context::{SharedStyleContext, ThreadLocalStyleContextCreationInfo};
|
||||
use style::context::{
|
||||
QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
|
||||
};
|
||||
use style::dom::{TDocument, TElement, TNode};
|
||||
use style::driver;
|
||||
use style::error_reporting::RustLogReporter;
|
||||
|
@ -159,13 +159,6 @@ pub struct LayoutThread {
|
|||
/// This can be used to easily check for invalid stale data.
|
||||
generation: Cell<u32>,
|
||||
|
||||
/// A channel on which new animations that have been triggered by style recalculation can be
|
||||
/// sent.
|
||||
new_animations_sender: Sender<Animation>,
|
||||
|
||||
/// Receives newly-discovered animations.
|
||||
_new_animations_receiver: Receiver<Animation>,
|
||||
|
||||
/// The number of Web fonts that have been requested but not yet loaded.
|
||||
outstanding_web_fonts: Arc<AtomicUsize>,
|
||||
|
||||
|
@ -499,9 +492,6 @@ impl LayoutThread {
|
|||
window_size.device_pixel_ratio,
|
||||
);
|
||||
|
||||
// Create the channel on which new animations can be sent.
|
||||
let (new_animations_sender, new_animations_receiver) = unbounded();
|
||||
|
||||
// Proxy IPC messages from the pipeline to the layout thread.
|
||||
let pipeline_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(pipeline_port);
|
||||
|
||||
|
@ -528,8 +518,6 @@ impl LayoutThread {
|
|||
font_cache_receiver: font_cache_receiver,
|
||||
font_cache_sender: ipc_font_cache_sender,
|
||||
generation: Cell::new(0),
|
||||
new_animations_sender: new_animations_sender,
|
||||
_new_animations_receiver: new_animations_receiver,
|
||||
outstanding_web_fonts: Arc::new(AtomicUsize::new(0)),
|
||||
box_tree_root: Default::default(),
|
||||
fragment_tree_root: Default::default(),
|
||||
|
@ -596,9 +584,6 @@ impl LayoutThread {
|
|||
snapshot_map: &'a SnapshotMap,
|
||||
origin: ImmutableOrigin,
|
||||
) -> LayoutContext<'a> {
|
||||
let thread_local_style_context_creation_data =
|
||||
ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone());
|
||||
|
||||
LayoutContext {
|
||||
id: self.id,
|
||||
origin,
|
||||
|
@ -607,11 +592,8 @@ impl LayoutThread {
|
|||
options: GLOBAL_STYLE_DATA.options.clone(),
|
||||
guards,
|
||||
visited_styles_enabled: false,
|
||||
running_animations: Default::default(),
|
||||
expired_animations: Default::default(),
|
||||
cancelled_animations: Default::default(),
|
||||
animation_states: Default::default(),
|
||||
registered_speculative_painters: &self.registered_painters,
|
||||
local_context_creation_data: Mutex::new(thread_local_style_context_creation_data),
|
||||
timer: self.timer.clone(),
|
||||
traversal_flags: TraversalFlags::empty(),
|
||||
snapshot_map: snapshot_map,
|
||||
|
|
|
@ -19,7 +19,7 @@ doctest = false
|
|||
gecko = ["style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml"]
|
||||
servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
|
||||
"cssparser/serde", "encoding_rs", "malloc_size_of/servo", "servo_url",
|
||||
"string_cache", "crossbeam-channel", "to_shmem/servo",
|
||||
"string_cache", "to_shmem/servo",
|
||||
"servo_arc/servo"]
|
||||
servo-layout-2013 = []
|
||||
servo-layout-2020 = []
|
||||
|
@ -34,7 +34,6 @@ atomic_refcell = "0.1"
|
|||
bitflags = "1.0"
|
||||
byteorder = "1.0"
|
||||
cssparser = "0.27"
|
||||
crossbeam-channel = { version = "0.4", optional = true }
|
||||
derive_more = "0.99"
|
||||
new_debug_unreachable = "1.0"
|
||||
encoding_rs = {version = "0.8", optional = true}
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
//! CSS transitions and animations.
|
||||
|
||||
// NOTE(emilio): This code isn't really executed in Gecko, but we don't want to
|
||||
// compile it out so that people remember it exists, thus the cfg'd Sender
|
||||
// import.
|
||||
// compile it out so that people remember it exists.
|
||||
|
||||
use crate::bezier::Bezier;
|
||||
use crate::context::SharedStyleContext;
|
||||
|
@ -15,7 +14,6 @@ use crate::font_metrics::FontMetricsProvider;
|
|||
use crate::properties::animated_properties::AnimatedProperty;
|
||||
use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
|
||||
use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::properties::LonghandIdSet;
|
||||
use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
|
||||
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||
|
@ -26,12 +24,8 @@ use crate::values::computed::TimingFunction;
|
|||
use crate::values::generics::box_::AnimationIterationCount;
|
||||
use crate::values::generics::easing::{StepPosition, TimingFunction as GenericTimingFunction};
|
||||
use crate::Atom;
|
||||
#[cfg(feature = "servo")]
|
||||
use crossbeam_channel::Sender;
|
||||
use servo_arc::Arc;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "gecko")]
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
/// This structure represents a keyframes animation current iteration state.
|
||||
///
|
||||
|
@ -369,70 +363,168 @@ impl PropertyAnimation {
|
|||
}
|
||||
}
|
||||
|
||||
/// Start any new transitions for this node and ensure that any existing transitions
|
||||
/// that are cancelled are marked as cancelled in the SharedStyleContext. This is
|
||||
/// at the end of calculating style for a single node.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn update_transitions(
|
||||
context: &SharedStyleContext,
|
||||
new_animations_sender: &Sender<Animation>,
|
||||
opaque_node: OpaqueNode,
|
||||
old_style: &ComputedValues,
|
||||
new_style: &mut Arc<ComputedValues>,
|
||||
expired_transitions: &[PropertyAnimation],
|
||||
) {
|
||||
let mut all_running_animations = context.running_animations.write();
|
||||
let previously_running_animations = all_running_animations
|
||||
.remove(&opaque_node)
|
||||
.unwrap_or_else(Vec::new);
|
||||
/// Holds the animation state for a particular element.
|
||||
#[derive(Default)]
|
||||
pub struct ElementAnimationState {
|
||||
/// The animations running for this element.
|
||||
pub running_animations: Vec<Animation>,
|
||||
|
||||
let properties_that_transition = start_transitions_if_applicable(
|
||||
context,
|
||||
new_animations_sender,
|
||||
opaque_node,
|
||||
old_style,
|
||||
new_style,
|
||||
expired_transitions,
|
||||
&previously_running_animations,
|
||||
);
|
||||
/// The animations that have finished for this element, but not canceled. These are cleared
|
||||
/// upon triggering a DOM event for each finished animation.
|
||||
pub finished_animations: Vec<Animation>,
|
||||
|
||||
let mut all_cancelled_animations = context.cancelled_animations.write();
|
||||
let mut cancelled_animations = all_cancelled_animations
|
||||
.remove(&opaque_node)
|
||||
.unwrap_or_else(Vec::new);
|
||||
let mut running_animations = vec![];
|
||||
/// The animations that have been cancelled for this element. These are cleared
|
||||
/// upon triggering a DOM event for each cancelled animation.
|
||||
pub cancelled_animations: Vec<Animation>,
|
||||
|
||||
// For every animation that was running before this style change, we cancel it
|
||||
// if the property no longer transitions.
|
||||
for running_animation in previously_running_animations.into_iter() {
|
||||
if let Animation::Transition(_, _, ref property_animation) = running_animation {
|
||||
if !properties_that_transition.contains(property_animation.property_id()) {
|
||||
cancelled_animations.push(running_animation);
|
||||
continue;
|
||||
/// New animations created for this element.
|
||||
pub new_animations: Vec<Animation>,
|
||||
}
|
||||
|
||||
impl ElementAnimationState {
|
||||
/// Cancel all animations in this `ElementAnimationState`. This is typically called
|
||||
/// when the element has been removed from the DOM.
|
||||
pub fn cancel_all_animations(&mut self) {
|
||||
self.cancelled_animations.extend(
|
||||
self.finished_animations
|
||||
.drain(..)
|
||||
.chain(self.running_animations.drain(..))
|
||||
.chain(self.new_animations.drain(..)),
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_transitions_with_nontransitioning_properties(
|
||||
&mut self,
|
||||
properties_that_transition: &LonghandIdSet,
|
||||
) {
|
||||
if self.running_animations.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let previously_running_transitions =
|
||||
std::mem::replace(&mut self.running_animations, Vec::new());
|
||||
for running_animation in previously_running_transitions {
|
||||
if let Animation::Transition(_, _, ref property_animation) = running_animation {
|
||||
if !properties_that_transition.contains(property_animation.property_id()) {
|
||||
self.cancelled_animations.push(running_animation);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
self.running_animations.push(running_animation);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_transition_with_same_end_value(&self, property_animation: &PropertyAnimation) -> bool {
|
||||
if self
|
||||
.running_animations
|
||||
.iter()
|
||||
.any(|animation| animation.is_transition_with_same_end_value(&property_animation))
|
||||
{
|
||||
debug!(
|
||||
"Running transition found with the same end value for {:?}",
|
||||
property_animation,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if self
|
||||
.finished_animations
|
||||
.iter()
|
||||
.any(|animation| animation.is_transition_with_same_end_value(&property_animation))
|
||||
{
|
||||
debug!(
|
||||
"Expired transition found with the same end value for {:?}",
|
||||
property_animation,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Compute before-change-style given an existing ElementAnimationState,
|
||||
/// information from the StyleContext, and the values of the previous style
|
||||
/// computation.
|
||||
///
|
||||
/// TODO(mrobinson): This is not a correct computation of before-change-style.
|
||||
/// For starters it's unclear why we aren't using the running transitions to
|
||||
/// transform this style into before-change-style.
|
||||
pub(crate) fn compute_before_change_style<E>(
|
||||
&mut self,
|
||||
context: &SharedStyleContext,
|
||||
style: &mut Arc<ComputedValues>,
|
||||
font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
for animation in self.finished_animations.iter() {
|
||||
debug!("Updating style for finished animation {:?}", animation);
|
||||
// TODO: support animation-fill-mode
|
||||
if let Animation::Transition(_, _, property_animation) = animation {
|
||||
property_animation.update(Arc::make_mut(style), 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
for running_animation in self.running_animations.iter_mut() {
|
||||
let update = match *running_animation {
|
||||
Animation::Transition(..) => continue,
|
||||
Animation::Keyframes(..) => {
|
||||
update_style_for_animation::<E>(context, running_animation, style, font_metrics)
|
||||
},
|
||||
};
|
||||
|
||||
match *running_animation {
|
||||
Animation::Transition(..) => unreachable!(),
|
||||
Animation::Keyframes(_, _, _, ref mut state) => match update {
|
||||
AnimationUpdate::Regular => {},
|
||||
AnimationUpdate::AnimationCanceled => {
|
||||
state.expired = true;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
running_animations.push(running_animation);
|
||||
}
|
||||
|
||||
if !cancelled_animations.is_empty() {
|
||||
all_cancelled_animations.insert(opaque_node, cancelled_animations);
|
||||
/// Whether this `ElementAnimationState` is empty, which means it doesn't
|
||||
/// hold any animations in any state.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.running_animations.is_empty() &&
|
||||
self.finished_animations.is_empty() &&
|
||||
self.cancelled_animations.is_empty() &&
|
||||
self.new_animations.is_empty()
|
||||
}
|
||||
if !running_animations.is_empty() {
|
||||
all_running_animations.insert(opaque_node, running_animations);
|
||||
|
||||
fn add_new_animation(&mut self, animation: Animation) {
|
||||
self.new_animations.push(animation);
|
||||
}
|
||||
|
||||
fn add_or_update_new_animation(&mut self, timer: &Timer, new_animation: Animation) {
|
||||
// If the animation was already present in the list for the node,
|
||||
// just update its state.
|
||||
if let Animation::Keyframes(_, _, ref new_name, ref new_state) = new_animation {
|
||||
for existing_animation in self.running_animations.iter_mut() {
|
||||
match existing_animation {
|
||||
Animation::Keyframes(_, _, ref name, ref mut state) if *name == *new_name => {
|
||||
state.update_from_other(&new_state, timer);
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise just add the new running animation.
|
||||
self.add_new_animation(new_animation);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kick off any new transitions for this node and return all of the properties that are
|
||||
/// transitioning. This is at the end of calculating style for a single node.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn start_transitions_if_applicable(
|
||||
context: &SharedStyleContext,
|
||||
new_animations_sender: &Sender<Animation>,
|
||||
opaque_node: OpaqueNode,
|
||||
old_style: &ComputedValues,
|
||||
new_style: &mut Arc<ComputedValues>,
|
||||
expired_transitions: &[PropertyAnimation],
|
||||
running_animations: &[Animation],
|
||||
animation_state: &mut ElementAnimationState,
|
||||
) -> LonghandIdSet {
|
||||
use crate::properties::animated_properties::TransitionPropertyIteration;
|
||||
|
||||
|
@ -473,30 +565,10 @@ pub fn start_transitions_if_applicable(
|
|||
property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0);
|
||||
|
||||
// Per [1], don't trigger a new transition if the end state for that
|
||||
// transition is the same as that of a transition that's expired.
|
||||
// transition is the same as that of a transition that's running or
|
||||
// completed.
|
||||
// [1]: https://drafts.csswg.org/css-transitions/#starting
|
||||
debug!("checking {:?} for matching end value", expired_transitions);
|
||||
if expired_transitions
|
||||
.iter()
|
||||
.any(|animation| animation.has_the_same_end_value_as(&property_animation))
|
||||
{
|
||||
debug!(
|
||||
"Not initiating transition for {}, expired transition \
|
||||
found with the same end value",
|
||||
property_animation.property_name()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if running_animations
|
||||
.iter()
|
||||
.any(|animation| animation.is_transition_with_same_end_value(&property_animation))
|
||||
{
|
||||
debug!(
|
||||
"Not initiating transition for {}, running transition \
|
||||
found with the same end value",
|
||||
property_animation.property_name()
|
||||
);
|
||||
if animation_state.has_transition_with_same_end_value(&property_animation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -505,13 +577,11 @@ pub fn start_transitions_if_applicable(
|
|||
let box_style = new_style.get_box();
|
||||
let now = context.timer.seconds();
|
||||
let start_time = now + (box_style.transition_delay_mod(transition.index).seconds() as f64);
|
||||
new_animations_sender
|
||||
.send(Animation::Transition(
|
||||
opaque_node,
|
||||
start_time,
|
||||
property_animation,
|
||||
))
|
||||
.unwrap();
|
||||
animation_state.add_new_animation(Animation::Transition(
|
||||
opaque_node,
|
||||
start_time,
|
||||
property_animation,
|
||||
));
|
||||
}
|
||||
|
||||
properties_that_transition
|
||||
|
@ -575,15 +645,12 @@ where
|
|||
pub fn maybe_start_animations<E>(
|
||||
element: E,
|
||||
context: &SharedStyleContext,
|
||||
new_animations_sender: &Sender<Animation>,
|
||||
node: OpaqueNode,
|
||||
new_style: &Arc<ComputedValues>,
|
||||
) -> bool
|
||||
where
|
||||
animation_state: &mut ElementAnimationState,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
let mut had_animations = false;
|
||||
|
||||
let box_style = new_style.get_box();
|
||||
for (i, name) in box_style.animation_name_iter().enumerate() {
|
||||
let name = match name.as_atom() {
|
||||
|
@ -637,8 +704,9 @@ where
|
|||
AnimationPlayState::Running => KeyframesRunningState::Running,
|
||||
};
|
||||
|
||||
new_animations_sender
|
||||
.send(Animation::Keyframes(
|
||||
animation_state.add_or_update_new_animation(
|
||||
&context.timer,
|
||||
Animation::Keyframes(
|
||||
node,
|
||||
anim.clone(),
|
||||
name.clone(),
|
||||
|
@ -653,12 +721,9 @@ where
|
|||
expired: false,
|
||||
cascade_style: new_style.clone(),
|
||||
},
|
||||
))
|
||||
.unwrap();
|
||||
had_animations = true;
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
had_animations
|
||||
}
|
||||
|
||||
/// Returns the kind of animation update that happened.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! The context within which style is calculated.
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::animation::Animation;
|
||||
use crate::animation::ElementAnimationState;
|
||||
use crate::bloom::StyleBloom;
|
||||
use crate::data::{EagerPseudoStyles, ElementData};
|
||||
#[cfg(feature = "servo")]
|
||||
|
@ -29,8 +29,6 @@ use crate::timer::Timer;
|
|||
use crate::traversal::DomTraversal;
|
||||
use crate::traversal_flags::TraversalFlags;
|
||||
use app_units::Au;
|
||||
#[cfg(feature = "servo")]
|
||||
use crossbeam_channel::Sender;
|
||||
use euclid::default::Size2D;
|
||||
use euclid::Scale;
|
||||
use fxhash::FxHashMap;
|
||||
|
@ -43,8 +41,6 @@ use servo_arc::Arc;
|
|||
use servo_atoms::Atom;
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
#[cfg(feature = "servo")]
|
||||
use std::sync::Mutex;
|
||||
use style_traits::CSSPixel;
|
||||
use style_traits::DevicePixel;
|
||||
#[cfg(feature = "servo")]
|
||||
|
@ -54,22 +50,6 @@ use uluru::{Entry, LRUCache};
|
|||
|
||||
pub use selectors::matching::QuirksMode;
|
||||
|
||||
/// This structure is used to create a local style context from a shared one.
|
||||
#[cfg(feature = "servo")]
|
||||
pub struct ThreadLocalStyleContextCreationInfo {
|
||||
new_animations_sender: Sender<Animation>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
impl ThreadLocalStyleContextCreationInfo {
|
||||
/// Trivially constructs a `ThreadLocalStyleContextCreationInfo`.
|
||||
pub fn new(animations_sender: Sender<Animation>) -> Self {
|
||||
ThreadLocalStyleContextCreationInfo {
|
||||
new_animations_sender: animations_sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A global options structure for the style system. We use this instead of
|
||||
/// opts to abstract across Gecko and Servo.
|
||||
#[derive(Clone)]
|
||||
|
@ -186,25 +166,13 @@ pub struct SharedStyleContext<'a> {
|
|||
/// A map with our snapshots in order to handle restyle hints.
|
||||
pub snapshot_map: &'a SnapshotMap,
|
||||
|
||||
/// The animations that are currently running.
|
||||
/// The state of all animations for our styled elements.
|
||||
#[cfg(feature = "servo")]
|
||||
pub running_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
|
||||
/// The list of animations that have expired since the last style recalculation.
|
||||
#[cfg(feature = "servo")]
|
||||
pub expired_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
|
||||
/// The list of animations that have expired since the last style recalculation.
|
||||
#[cfg(feature = "servo")]
|
||||
pub cancelled_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>,
|
||||
pub animation_states: Arc<RwLock<FxHashMap<OpaqueNode, ElementAnimationState>>>,
|
||||
|
||||
/// Paint worklets
|
||||
#[cfg(feature = "servo")]
|
||||
pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
|
||||
|
||||
/// Data needed to create the thread-local style context from the shared one.
|
||||
#[cfg(feature = "servo")]
|
||||
pub local_context_creation_data: Mutex<ThreadLocalStyleContextCreationInfo>,
|
||||
}
|
||||
|
||||
impl<'a> SharedStyleContext<'a> {
|
||||
|
@ -741,10 +709,6 @@ pub struct ThreadLocalStyleContext<E: TElement> {
|
|||
pub rule_cache: RuleCache,
|
||||
/// The bloom filter used to fast-reject selector-matching.
|
||||
pub bloom_filter: StyleBloom<E>,
|
||||
/// A channel on which new animations that have been triggered by style
|
||||
/// recalculation can be sent.
|
||||
#[cfg(feature = "servo")]
|
||||
pub new_animations_sender: Sender<Animation>,
|
||||
/// A set of tasks to be run (on the parent thread) in sequential mode after
|
||||
/// the rest of the styling is complete. This is useful for
|
||||
/// infrequently-needed non-threadsafe operations.
|
||||
|
@ -778,12 +742,6 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
|
|||
sharing_cache: StyleSharingCache::new(),
|
||||
rule_cache: RuleCache::new(),
|
||||
bloom_filter: StyleBloom::new(),
|
||||
new_animations_sender: shared
|
||||
.local_context_creation_data
|
||||
.lock()
|
||||
.unwrap()
|
||||
.new_animations_sender
|
||||
.clone(),
|
||||
tasks: SequentialTaskList(Vec::new()),
|
||||
selector_flags: SelectorFlagsMap::new(),
|
||||
statistics: PerThreadTraversalStatistics::default(),
|
||||
|
|
|
@ -32,8 +32,6 @@ extern crate atomic_refcell;
|
|||
extern crate bitflags;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate byteorder;
|
||||
#[cfg(feature = "servo")]
|
||||
extern crate crossbeam_channel;
|
||||
#[macro_use]
|
||||
extern crate cssparser;
|
||||
#[macro_use]
|
||||
|
|
|
@ -7,13 +7,15 @@
|
|||
#![allow(unsafe_code)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::animation;
|
||||
use crate::computed_value_flags::ComputedValueFlags;
|
||||
use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
|
||||
use crate::context::{SharedStyleContext, StyleContext};
|
||||
use crate::data::ElementData;
|
||||
use crate::dom::TElement;
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::dom::{OpaqueNode, TNode};
|
||||
use crate::dom::TNode;
|
||||
use crate::invalidation::element::restyle_hints::RestyleHint;
|
||||
use crate::properties::longhands::display::computed_value::T as Display;
|
||||
use crate::properties::ComputedValues;
|
||||
|
@ -437,55 +439,48 @@ trait PrivateMatchMethods: TElement {
|
|||
_restyle_hint: RestyleHint,
|
||||
_important_rules_changed: bool,
|
||||
) {
|
||||
use crate::animation;
|
||||
|
||||
let this_opaque = self.as_node().opaque();
|
||||
let mut expired_transitions = vec![];
|
||||
let shared_context = context.shared;
|
||||
if let Some(ref mut old_values) = *old_values {
|
||||
// We apply the expired transitions and animations to the old style
|
||||
// here, because we later compare the old style to the new style in
|
||||
// `start_transitions_if_applicable`. If the styles differ then it will
|
||||
// cause the expired transition to restart.
|
||||
//
|
||||
// TODO(mrobinson): We should really be following spec behavior and calculate
|
||||
// after-change-style and before-change-style here.
|
||||
Self::collect_and_update_style_for_expired_transitions(
|
||||
shared_context,
|
||||
this_opaque,
|
||||
old_values,
|
||||
&mut expired_transitions,
|
||||
);
|
||||
let mut animation_states = shared_context.animation_states.write();
|
||||
let mut animation_state = animation_states.remove(&this_opaque).unwrap_or_default();
|
||||
|
||||
Self::update_style_for_animations(
|
||||
if let Some(ref mut old_values) = *old_values {
|
||||
animation_state.compute_before_change_style::<Self>(
|
||||
shared_context,
|
||||
this_opaque,
|
||||
old_values,
|
||||
&context.thread_local.font_metrics_provider,
|
||||
);
|
||||
}
|
||||
|
||||
let new_animations_sender = &context.thread_local.new_animations_sender;
|
||||
// Trigger any present animations if necessary.
|
||||
animation::maybe_start_animations(
|
||||
*self,
|
||||
&shared_context,
|
||||
new_animations_sender,
|
||||
this_opaque,
|
||||
&new_values,
|
||||
&mut animation_state,
|
||||
);
|
||||
|
||||
// Trigger transitions if necessary. This will set `new_values` to
|
||||
// the starting value of the transition if it did trigger a transition.
|
||||
if let Some(ref values) = old_values {
|
||||
animation::update_transitions(
|
||||
&shared_context,
|
||||
new_animations_sender,
|
||||
if let Some(ref old_values) = old_values {
|
||||
let transitioning_properties = animation::start_transitions_if_applicable(
|
||||
shared_context,
|
||||
this_opaque,
|
||||
&values,
|
||||
old_values,
|
||||
new_values,
|
||||
&expired_transitions,
|
||||
&mut animation_state,
|
||||
);
|
||||
animation_state
|
||||
.cancel_transitions_with_nontransitioning_properties(&transitioning_properties);
|
||||
}
|
||||
|
||||
animation_state.finished_animations.clear();
|
||||
|
||||
// If the ElementAnimationState is empty, don't push it to save
|
||||
// memory and to avoid extra processing later.
|
||||
if !animation_state.is_empty() {
|
||||
animation_states.insert(this_opaque, animation_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -601,67 +596,6 @@ trait PrivateMatchMethods: TElement {
|
|||
// properties, we can stop the cascade.
|
||||
ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
fn collect_and_update_style_for_expired_transitions(
|
||||
context: &SharedStyleContext,
|
||||
node: OpaqueNode,
|
||||
style: &mut Arc<ComputedValues>,
|
||||
expired_transitions: &mut Vec<crate::animation::PropertyAnimation>,
|
||||
) {
|
||||
use crate::animation::Animation;
|
||||
|
||||
let mut all_expired_animations = context.expired_animations.write();
|
||||
if let Some(animations) = all_expired_animations.remove(&node) {
|
||||
debug!("removing expired animations for {:?}", node);
|
||||
for animation in animations {
|
||||
debug!("Updating expired animation {:?}", animation);
|
||||
// TODO: support animation-fill-mode
|
||||
if let Animation::Transition(_, _, property_animation) = animation {
|
||||
property_animation.update(Arc::make_mut(style), 1.0);
|
||||
expired_transitions.push(property_animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
fn update_style_for_animations(
|
||||
context: &SharedStyleContext,
|
||||
node: OpaqueNode,
|
||||
style: &mut Arc<ComputedValues>,
|
||||
font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
|
||||
) {
|
||||
use crate::animation::{self, Animation, AnimationUpdate};
|
||||
|
||||
let mut all_running_animations = context.running_animations.write();
|
||||
let running_animations = match all_running_animations.get_mut(&node) {
|
||||
Some(running_animations) => running_animations,
|
||||
None => return,
|
||||
};
|
||||
|
||||
for running_animation in running_animations.iter_mut() {
|
||||
let update = match *running_animation {
|
||||
Animation::Transition(..) => continue,
|
||||
Animation::Keyframes(..) => animation::update_style_for_animation::<Self>(
|
||||
context,
|
||||
running_animation,
|
||||
style,
|
||||
font_metrics,
|
||||
),
|
||||
};
|
||||
|
||||
match *running_animation {
|
||||
Animation::Transition(..) => unreachable!(),
|
||||
Animation::Keyframes(_, _, _, ref mut state) => match update {
|
||||
AnimationUpdate::Regular => {},
|
||||
AnimationUpdate::AnimationCanceled => {
|
||||
state.expired = true;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: TElement> PrivateMatchMethods for E {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue