diff options
-rw-r--r-- | components/atoms/static_atoms.txt | 2 | ||||
-rw-r--r-- | components/layout/animation.rs | 133 | ||||
-rw-r--r-- | components/layout_thread/lib.rs | 13 | ||||
-rw-r--r-- | components/script/dom/event.rs | 1 | ||||
-rw-r--r-- | components/script/dom/macros.rs | 1 | ||||
-rw-r--r-- | components/script/dom/webidls/EventHandler.webidl | 1 | ||||
-rw-r--r-- | components/script/script_thread.rs | 17 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 4 | ||||
-rw-r--r-- | tests/wpt/metadata/css/css-transitions/before-load-001.html.ini | 4 | ||||
-rw-r--r-- | tests/wpt/metadata/css/css-transitions/idlharness.html.ini | 18 | ||||
-rw-r--r-- | tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini | 4 |
11 files changed, 105 insertions, 93 deletions
diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index 1d00cc24214..ebf4a2c27be 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -131,6 +131,7 @@ toggle track transitioncancel transitionend +transitionrun unhandledrejection unload url @@ -142,5 +143,6 @@ webkitAnimationEnd webkitAnimationIteration webkitAnimationStart webkitTransitionEnd +webkitTransitionRun week width diff --git a/components/layout/animation.rs b/components/layout/animation.rs index 185b6d9c9d1..57db0abda42 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -15,54 +15,62 @@ use script_traits::UntrustedNodeAddress; use script_traits::{ AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg, TransitionEventType, }; -use style::animation::{update_style_for_animation, Animation, ElementAnimationState}; +use style::animation::{ + update_style_for_animation, Animation, ElementAnimationState, PropertyAnimation, +}; use style::dom::TElement; use style::font_metrics::ServoMetricsProvider; use style::selector_parser::RestyleDamage; use style::timer::Timer; +/// Collect newly transitioning nodes, which is used by the script process during +/// forced, synchronous reflows to root DOM nodes for the duration of their transitions. +pub fn collect_newly_transitioning_nodes( + animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>, + mut out: Option<&mut Vec<UntrustedNodeAddress>>, +) { + // This extends the output vector with an iterator that contains a copy of the node + // address for every new animation. This is a bit goofy, but the script thread + // currently stores a rooted node for every property that is transitioning. + if let Some(ref mut out) = out { + out.extend(animation_states.iter().flat_map(|(node, state)| { + let num_transitions = state + .new_animations + .iter() + .filter(|animation| animation.is_transition()) + .count(); + std::iter::repeat(node.to_untrusted_node_address()).take(num_transitions) + })); + } +} + /// 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>( +pub fn update_animation_states( constellation_chan: &IpcSender<ConstellationMsg>, script_chan: &IpcSender<ConstellationControlMsg>, animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationState>, invalid_nodes: FxHashSet<OpaqueNode>, - mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>, pipeline_id: PipelineId, timer: &Timer, -) where - E: TElement, -{ +) { let had_running_animations = animation_states .values() .any(|state| !state.running_animations.is_empty()); + // Cancel all animations on any invalid nodes. These entries will later + // be removed from the list of states, because their states will become + // empty. 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); } } let now = timer.seconds(); 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); - + update_animation_state(script_chan, animation_state, pipeline_id, now, *node); have_running_animations = have_running_animations || !animation_state.running_animations.is_empty(); } @@ -85,16 +93,37 @@ pub fn update_animation_states<E>( .unwrap(); } -pub fn update_animation_state<E>( - script_chan: &IpcSender<ConstellationControlMsg>, +pub fn update_animation_state( + script_channel: &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); + node: OpaqueNode, +) { + let send_transition_event = |property_animation: &PropertyAnimation, event_type| { + script_channel + .send(ConstellationControlMsg::TransitionEvent { + pipeline_id, + event_type, + node: node.to_untrusted_node_address(), + property_name: property_animation.property_name().into(), + elapsed_time: property_animation.duration, + }) + .unwrap() + }; + handle_cancelled_animations(animation_state, send_transition_event); + handle_running_animations(animation_state, now, send_transition_event); + handle_new_animations(animation_state, send_transition_event); +} + +/// Walk through the list of running animations and remove all of the ones that +/// have ended. +pub fn handle_running_animations( + animation_state: &mut ElementAnimationState, + now: f64, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), +) { let mut running_animations = std::mem::replace(&mut animation_state.running_animations, Vec::new()); for mut running_animation in running_animations.drain(..) { @@ -115,46 +144,25 @@ pub fn update_animation_state<E>( 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 { - pipeline_id, - event_type: TransitionEventType::TransitionEnd, - node: node.to_untrusted_node_address(), - property_name: property_animation.property_name().into(), - elapsed_time: property_animation.duration, - }) - .unwrap(); + if let Animation::Transition(_, _, ref property_animation) = running_animation { + send_transition_event(property_animation, TransitionEventType::TransitionEnd); } animation_state.finished_animations.push(running_animation); } } - - animation_state - .running_animations - .append(&mut animation_state.new_animations); } /// Send events for cancelled animations. Currently this only handles cancelled /// transitions, but eventually this should handle cancelled CSS animations as /// well. -pub fn send_events_for_cancelled_animations( - script_channel: &IpcSender<ConstellationControlMsg>, +pub fn handle_cancelled_animations( animation_state: &mut ElementAnimationState, - pipeline_id: PipelineId, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), ) { 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::Transition(_, _, ref property_animation) => { + send_transition_event(property_animation, TransitionEventType::TransitionCancel) }, Animation::Keyframes(..) => { warn!("Got unexpected animation in finished transitions list.") @@ -163,6 +171,21 @@ pub fn send_events_for_cancelled_animations( } } +pub fn handle_new_animations( + animation_state: &mut ElementAnimationState, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), +) { + for animation in animation_state.new_animations.drain(..) { + match animation { + Animation::Transition(_, _, ref property_animation) => { + send_transition_event(property_animation, TransitionEventType::TransitionRun) + }, + Animation::Keyframes(..) => {}, + } + animation_state.running_animations.push(animation); + } +} + /// Recalculates style for a set of animations. This does *not* run with the DOM /// lock held. Returns a set of nodes associated with animations that are no longer /// valid. diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 6272147b65e..4a346315e8a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -1762,13 +1762,18 @@ impl LayoutThread { .map(|nodes| nodes.lock().unwrap()); 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_states::<ServoLayoutElement>( + let mut animation_states = self.animation_states.write(); + + animation::collect_newly_transitioning_nodes( + &animation_states, + newly_transitioning_nodes, + ); + + animation::update_animation_states( &self.constellation_chan, &self.script_chan, - &mut *self.animation_states.write(), + &mut *animation_states, invalid_nodes, - newly_transitioning_nodes, self.id, &self.timer, ); diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index aba0179a59b..3a0e7490fc9 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -647,6 +647,7 @@ fn invoke( atom!("animationiteration") => Some(atom!("webkitAnimationIteration")), atom!("animationstart") => Some(atom!("webkitAnimationStart")), atom!("transitionend") => Some(atom!("webkitTransitionEnd")), + atom!("transitionrun") => Some(atom!("webkitTransitionRun")), _ => None, } { let original_type = event.type_(); diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index df0a929a0ff..b1ea4f5cde2 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -498,6 +498,7 @@ macro_rules! global_event_handlers( event_handler!(toggle, GetOntoggle, SetOntoggle); event_handler!(transitioncancel, GetOntransitioncancel, SetOntransitioncancel); event_handler!(transitionend, GetOntransitionend, SetOntransitionend); + event_handler!(transitionrun, GetOntransitionrun, SetOntransitionrun); event_handler!(volumechange, GetOnvolumechange, SetOnvolumechange); event_handler!(waiting, GetOnwaiting, SetOnwaiting); ) diff --git a/components/script/dom/webidls/EventHandler.webidl b/components/script/dom/webidls/EventHandler.webidl index 772c47facd2..a3514182806 100644 --- a/components/script/dom/webidls/EventHandler.webidl +++ b/components/script/dom/webidls/EventHandler.webidl @@ -92,6 +92,7 @@ interface mixin GlobalEventHandlers { // https://drafts.csswg.org/css-transitions/#interface-globaleventhandlers-idl partial interface mixin GlobalEventHandlers { + attribute EventHandler ontransitionrun; attribute EventHandler ontransitionend; attribute EventHandler ontransitioncancel; }; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 7a672851ba6..606ac81493e 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -2920,22 +2920,20 @@ impl ScriptThread { let js_runtime = self.js_runtime.rt(); let node = unsafe { from_untrusted_node_address(js_runtime, unsafe_node) }; - let idx = self + let node_index = self .transitioning_nodes .borrow() .iter() .position(|n| &**n as *const _ == &*node as *const _); - match idx { - Some(idx) => { - self.transitioning_nodes.borrow_mut().remove(idx); - }, + let node_index = match node_index { + Some(node_index) => node_index, None => { // If no index is found, we can't know whether this node is safe to use. // It's better not to fire a DOM event than crash. warn!("Ignoring transition end notification for unknown node."); return; }, - } + }; if self.closed_pipelines.borrow().contains(&pipeline_id) { warn!("Ignoring transition event for closed pipeline."); @@ -2943,12 +2941,17 @@ impl ScriptThread { } let event_atom = match event_type { + TransitionEventType::TransitionRun => atom!("transitionrun"), TransitionEventType::TransitionEnd => { // Not quite the right thing - see #13865. node.dirty(NodeDamage::NodeStyleDamaged); + self.transitioning_nodes.borrow_mut().remove(node_index); atom!("transitionend") }, - TransitionEventType::TransitionCancel => atom!("transitioncancel"), + TransitionEventType::TransitionCancel => { + self.transitioning_nodes.borrow_mut().remove(node_index); + atom!("transitioncancel") + }, }; let event_init = TransitionEventInit { diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 7eb60241f5d..3db883b6536 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -283,8 +283,10 @@ pub enum UpdatePipelineIdReason { } /// The type of transition event to trigger. -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub enum TransitionEventType { + /// The transition has started running. + TransitionRun, /// The transition has ended by reaching the end of its animation. TransitionEnd, /// The transition ended early for some reason, such as the property diff --git a/tests/wpt/metadata/css/css-transitions/before-load-001.html.ini b/tests/wpt/metadata/css/css-transitions/before-load-001.html.ini index 8b9fb0b259a..02d1d23876d 100644 --- a/tests/wpt/metadata/css/css-transitions/before-load-001.html.ini +++ b/tests/wpt/metadata/css/css-transitions/before-load-001.html.ini @@ -1,8 +1,4 @@ [before-load-001.html] - expected: TIMEOUT [transition height from 10px to 100px / events] expected: FAIL - [CSS Transitions Test: Transitioning before load event] - expected: TIMEOUT - diff --git a/tests/wpt/metadata/css/css-transitions/idlharness.html.ini b/tests/wpt/metadata/css/css-transitions/idlharness.html.ini index 21836fbea38..f69670e9632 100644 --- a/tests/wpt/metadata/css/css-transitions/idlharness.html.ini +++ b/tests/wpt/metadata/css/css-transitions/idlharness.html.ini @@ -1,34 +1,16 @@ [idlharness.html] - [Document interface: document must inherit property "ontransitionrun" with the proper type] - expected: FAIL - [HTMLElement interface: attribute ontransitionstart] expected: FAIL [Document interface: document must inherit property "ontransitionstart" with the proper type] expected: FAIL - [HTMLElement interface: attribute ontransitionrun] - expected: FAIL - - [Window interface: attribute ontransitionrun] - expected: FAIL - [HTMLElement interface: document must inherit property "ontransitionstart" with the proper type] expected: FAIL - [HTMLElement interface: document must inherit property "ontransitionrun" with the proper type] - expected: FAIL - - [Window interface: window must inherit property "ontransitionrun" with the proper type] - expected: FAIL - [Document interface: attribute ontransitionstart] expected: FAIL - [Document interface: attribute ontransitionrun] - expected: FAIL - [Window interface: window must inherit property "ontransitionstart" with the proper type] expected: FAIL diff --git a/tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini b/tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini index 887eaa90578..545eae021e5 100644 --- a/tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini +++ b/tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini @@ -1,8 +1,4 @@ [non-rendered-element-001.html] - expected: TIMEOUT - [Transitions are canceled when an element is no longer rendered] - expected: TIMEOUT - [Transitions do not run for an element newly rendered] expected: FAIL |