aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/atoms/static_atoms.txt2
-rw-r--r--components/layout/animation.rs133
-rw-r--r--components/layout_thread/lib.rs13
-rw-r--r--components/script/dom/event.rs1
-rw-r--r--components/script/dom/macros.rs1
-rw-r--r--components/script/dom/webidls/EventHandler.webidl1
-rw-r--r--components/script/script_thread.rs17
-rw-r--r--components/script_traits/lib.rs4
-rw-r--r--tests/wpt/metadata/css/css-transitions/before-load-001.html.ini4
-rw-r--r--tests/wpt/metadata/css/css-transitions/idlharness.html.ini18
-rw-r--r--tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini4
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