mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Add support for animationend event
This is triggered when an animation finishes. This is a high priority because it allows us to start rooting nodes with animations in the script thread. This doesn't yet cause a lot of tests to pass because they rely on the existence of `Document.getAnimations()` and the presence of `animationstart` and animationiteration` events.
This commit is contained in:
parent
6fb75c2b9e
commit
3903c1fb98
27 changed files with 335 additions and 331 deletions
|
@ -13,20 +13,20 @@ use ipc_channel::ipc::IpcSender;
|
|||
use msg::constellation_msg::PipelineId;
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use script_traits::{
|
||||
AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg, TransitionEventType,
|
||||
AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg,
|
||||
TransitionOrAnimationEventType,
|
||||
};
|
||||
use servo_arc::Arc;
|
||||
use style::animation::{
|
||||
update_style_for_animation, Animation, ElementAnimationState, PropertyAnimation,
|
||||
};
|
||||
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;
|
||||
|
||||
/// 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(
|
||||
/// 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, ElementAnimationState>,
|
||||
mut out: Option<&mut Vec<UntrustedNodeAddress>>,
|
||||
) {
|
||||
|
@ -35,12 +35,7 @@ pub fn collect_newly_transitioning_nodes(
|
|||
// 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)
|
||||
std::iter::repeat(node.to_untrusted_node_address()).take(state.new_animations.len())
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -101,21 +96,28 @@ pub fn update_animation_state(
|
|||
now: f64,
|
||||
node: OpaqueNode,
|
||||
) {
|
||||
let send_transition_event = |property_animation: &PropertyAnimation, event_type| {
|
||||
let send_event = |animation: &Animation, event_type, elapsed_time| {
|
||||
let property_or_animation_name = match *animation {
|
||||
Animation::Transition(_, _, ref property_animation) => {
|
||||
property_animation.property_name().into()
|
||||
},
|
||||
Animation::Keyframes(_, _, ref name, _) => name.to_string(),
|
||||
};
|
||||
|
||||
script_channel
|
||||
.send(ConstellationControlMsg::TransitionEvent {
|
||||
.send(ConstellationControlMsg::TransitionOrAnimationEvent {
|
||||
pipeline_id,
|
||||
event_type,
|
||||
node: node.to_untrusted_node_address(),
|
||||
property_name: property_animation.property_name().into(),
|
||||
elapsed_time: property_animation.duration,
|
||||
property_or_animation_name,
|
||||
elapsed_time,
|
||||
})
|
||||
.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);
|
||||
handle_cancelled_animations(animation_state, now, send_event);
|
||||
handle_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
|
||||
|
@ -123,7 +125,7 @@ pub fn update_animation_state(
|
|||
pub fn handle_running_animations(
|
||||
animation_state: &mut ElementAnimationState,
|
||||
now: f64,
|
||||
mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType),
|
||||
mut send_event: impl FnMut(&Animation, TransitionOrAnimationEventType, f64),
|
||||
) {
|
||||
let mut running_animations =
|
||||
std::mem::replace(&mut animation_state.running_animations, Vec::new());
|
||||
|
@ -144,9 +146,18 @@ pub fn handle_running_animations(
|
|||
animation_state.running_animations.push(running_animation);
|
||||
} else {
|
||||
debug!("Finishing transition: {:?}", running_animation);
|
||||
if let Animation::Transition(_, _, ref property_animation) = running_animation {
|
||||
send_transition_event(property_animation, TransitionEventType::TransitionEnd);
|
||||
}
|
||||
let (event_type, elapsed_time) = match running_animation {
|
||||
Animation::Transition(_, _, ref property_animation) => (
|
||||
TransitionOrAnimationEventType::TransitionEnd,
|
||||
property_animation.duration,
|
||||
),
|
||||
Animation::Keyframes(_, _, _, ref mut state) => (
|
||||
TransitionOrAnimationEventType::AnimationEnd,
|
||||
state.active_duration(),
|
||||
),
|
||||
};
|
||||
|
||||
send_event(&running_animation, event_type, elapsed_time);
|
||||
animation_state.finished_animations.push(running_animation);
|
||||
}
|
||||
}
|
||||
|
@ -157,12 +168,19 @@ pub fn handle_running_animations(
|
|||
/// well.
|
||||
pub fn handle_cancelled_animations(
|
||||
animation_state: &mut ElementAnimationState,
|
||||
mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType),
|
||||
now: f64,
|
||||
mut send_event: impl FnMut(&Animation, TransitionOrAnimationEventType, f64),
|
||||
) {
|
||||
for animation in animation_state.cancelled_animations.drain(..) {
|
||||
match animation {
|
||||
Animation::Transition(_, _, ref property_animation) => {
|
||||
send_transition_event(property_animation, TransitionEventType::TransitionCancel)
|
||||
Animation::Transition(_, start_time, _) => {
|
||||
// TODO(mrobinson): We need to properly compute the elapsed_time here
|
||||
// according to https://drafts.csswg.org/css-transitions/#event-transitionevent
|
||||
send_event(
|
||||
&animation,
|
||||
TransitionOrAnimationEventType::TransitionCancel,
|
||||
(now - start_time).max(0.),
|
||||
);
|
||||
},
|
||||
// TODO(mrobinson): We should send animationcancel events.
|
||||
Animation::Keyframes(..) => {},
|
||||
|
@ -172,12 +190,18 @@ pub fn handle_cancelled_animations(
|
|||
|
||||
pub fn handle_new_animations(
|
||||
animation_state: &mut ElementAnimationState,
|
||||
mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType),
|
||||
mut send_event: impl FnMut(&Animation, TransitionOrAnimationEventType, f64),
|
||||
) {
|
||||
for animation in animation_state.new_animations.drain(..) {
|
||||
match animation {
|
||||
Animation::Transition(_, _, ref property_animation) => {
|
||||
send_transition_event(property_animation, TransitionEventType::TransitionRun)
|
||||
Animation::Transition(..) => {
|
||||
// TODO(mrobinson): We need to properly compute the elapsed_time here
|
||||
// according to https://drafts.csswg.org/css-transitions/#event-transitionevent
|
||||
send_event(
|
||||
&animation,
|
||||
TransitionOrAnimationEventType::TransitionRun,
|
||||
0.,
|
||||
)
|
||||
},
|
||||
Animation::Keyframes(..) => {},
|
||||
}
|
||||
|
|
|
@ -86,9 +86,9 @@ pub struct LayoutContext<'a> {
|
|||
/// A None value means that this layout was not initiated by the script thread.
|
||||
pub pending_images: Option<Mutex<Vec<PendingImage>>>,
|
||||
|
||||
/// A list of nodes that have just initiated a CSS transition.
|
||||
/// A list of nodes that have just initiated a CSS transition or animation.
|
||||
/// A None value means that this layout was not initiated by the script thread.
|
||||
pub newly_transitioning_nodes: Option<Mutex<Vec<UntrustedNodeAddress>>>,
|
||||
pub newly_animating_nodes: Option<Mutex<Vec<UntrustedNodeAddress>>>,
|
||||
}
|
||||
|
||||
impl<'a> Drop for LayoutContext<'a> {
|
||||
|
|
|
@ -648,7 +648,7 @@ impl LayoutThread {
|
|||
} else {
|
||||
None
|
||||
},
|
||||
newly_transitioning_nodes: if script_initiated_layout {
|
||||
newly_animating_nodes: if script_initiated_layout {
|
||||
Some(Mutex::new(vec![]))
|
||||
} else {
|
||||
None
|
||||
|
@ -1565,11 +1565,11 @@ impl LayoutThread {
|
|||
};
|
||||
reflow_result.pending_images = pending_images;
|
||||
|
||||
let newly_transitioning_nodes = match context.newly_transitioning_nodes {
|
||||
let newly_animating_nodes = match context.newly_animating_nodes {
|
||||
Some(ref nodes) => std::mem::replace(&mut *nodes.lock().unwrap(), vec![]),
|
||||
None => vec![],
|
||||
};
|
||||
reflow_result.newly_transitioning_nodes = newly_transitioning_nodes;
|
||||
reflow_result.newly_animating_nodes = newly_animating_nodes;
|
||||
|
||||
let mut root_flow = match self.root_flow.borrow().clone() {
|
||||
Some(root_flow) => root_flow,
|
||||
|
@ -1741,7 +1741,7 @@ impl LayoutThread {
|
|||
invalid_nodes,
|
||||
);
|
||||
assert!(layout_context.pending_images.is_none());
|
||||
assert!(layout_context.newly_transitioning_nodes.is_none());
|
||||
assert!(layout_context.newly_animating_nodes.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1756,19 +1756,13 @@ impl LayoutThread {
|
|||
invalid_nodes: FxHashSet<OpaqueNode>,
|
||||
) {
|
||||
{
|
||||
let mut newly_transitioning_nodes = context
|
||||
.newly_transitioning_nodes
|
||||
let mut newly_animating_nodes = context
|
||||
.newly_animating_nodes
|
||||
.as_ref()
|
||||
.map(|nodes| nodes.lock().unwrap());
|
||||
let newly_transitioning_nodes =
|
||||
newly_transitioning_nodes.as_mut().map(|nodes| &mut **nodes);
|
||||
let newly_animating_nodes = newly_animating_nodes.as_mut().map(|nodes| &mut **nodes);
|
||||
let mut animation_states = self.animation_states.write();
|
||||
|
||||
animation::collect_newly_transitioning_nodes(
|
||||
&animation_states,
|
||||
newly_transitioning_nodes,
|
||||
);
|
||||
|
||||
animation::collect_newly_animating_nodes(&animation_states, newly_animating_nodes);
|
||||
animation::update_animation_states(
|
||||
&self.constellation_chan,
|
||||
&self.script_chan,
|
||||
|
|
76
components/script/dom/animationevent.rs
Normal file
76
components/script/dom/animationevent.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* 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/. */
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::AnimationEventBinding::{
|
||||
AnimationEventInit, AnimationEventMethods,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::reflector::reflect_dom_object;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::window::Window;
|
||||
use dom_struct::dom_struct;
|
||||
use servo_atoms::Atom;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct AnimationEvent {
|
||||
event: Event,
|
||||
animation_name: Atom,
|
||||
elapsed_time: Finite<f32>,
|
||||
pseudo_element: DOMString,
|
||||
}
|
||||
|
||||
impl AnimationEvent {
|
||||
fn new_inherited(init: &AnimationEventInit) -> AnimationEvent {
|
||||
AnimationEvent {
|
||||
event: Event::new_inherited(),
|
||||
animation_name: Atom::from(init.animationName.clone()),
|
||||
elapsed_time: init.elapsedTime.clone(),
|
||||
pseudo_element: init.pseudoElement.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(window: &Window, type_: Atom, init: &AnimationEventInit) -> DomRoot<AnimationEvent> {
|
||||
let ev = reflect_dom_object(Box::new(AnimationEvent::new_inherited(init)), window);
|
||||
{
|
||||
let event = ev.upcast::<Event>();
|
||||
event.init_event(type_, init.parent.bubbles, init.parent.cancelable);
|
||||
}
|
||||
ev
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Constructor(
|
||||
window: &Window,
|
||||
type_: DOMString,
|
||||
init: &AnimationEventInit,
|
||||
) -> DomRoot<AnimationEvent> {
|
||||
AnimationEvent::new(window, Atom::from(type_), init)
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimationEventMethods for AnimationEvent {
|
||||
// https://drafts.csswg.org/css-animations/#interface-animationevent-attributes
|
||||
fn AnimationName(&self) -> DOMString {
|
||||
DOMString::from(&*self.animation_name)
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-animations/#interface-animationevent-attributes
|
||||
fn ElapsedTime(&self) -> Finite<f32> {
|
||||
self.elapsed_time.clone()
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-animations/#interface-animationevent-attributes
|
||||
fn PseudoElement(&self) -> DOMString {
|
||||
self.pseudo_element.clone()
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-event-istrusted
|
||||
fn IsTrusted(&self) -> bool {
|
||||
self.upcast::<Event>().IsTrusted()
|
||||
}
|
||||
}
|
|
@ -442,6 +442,7 @@ macro_rules! global_event_handlers(
|
|||
);
|
||||
(NoOnload) => (
|
||||
event_handler!(abort, GetOnabort, SetOnabort);
|
||||
event_handler!(animationend, GetOnanimationend, SetOnanimationend);
|
||||
event_handler!(cancel, GetOncancel, SetOncancel);
|
||||
event_handler!(canplay, GetOncanplay, SetOncanplay);
|
||||
event_handler!(canplaythrough, GetOncanplaythrough, SetOncanplaythrough);
|
||||
|
|
|
@ -213,6 +213,7 @@ pub mod abstractworker;
|
|||
pub mod abstractworkerglobalscope;
|
||||
pub mod activation;
|
||||
pub mod analysernode;
|
||||
pub mod animationevent;
|
||||
pub mod attr;
|
||||
pub mod audiobuffer;
|
||||
pub mod audiobuffersourcenode;
|
||||
|
|
26
components/script/dom/webidls/AnimationEvent.webidl
Normal file
26
components/script/dom/webidls/AnimationEvent.webidl
Normal file
|
@ -0,0 +1,26 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* http://www.w3.org/TR/css3-animations/#animation-events-
|
||||
* http://dev.w3.org/csswg/css3-animations/#animation-events-
|
||||
*
|
||||
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Exposed=Window]
|
||||
interface AnimationEvent : Event {
|
||||
constructor(DOMString type, optional AnimationEventInit eventInitDict = {});
|
||||
|
||||
readonly attribute DOMString animationName;
|
||||
readonly attribute float elapsedTime;
|
||||
readonly attribute DOMString pseudoElement;
|
||||
};
|
||||
|
||||
dictionary AnimationEventInit : EventInit {
|
||||
DOMString animationName = "";
|
||||
float elapsedTime = 0;
|
||||
DOMString pseudoElement = "";
|
||||
};
|
|
@ -90,6 +90,11 @@ interface mixin GlobalEventHandlers {
|
|||
attribute EventHandler onwaiting;
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/css-animations/#interface-globaleventhandlers-idl
|
||||
partial interface mixin GlobalEventHandlers {
|
||||
attribute EventHandler onanimationend;
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/css-transitions/#interface-globaleventhandlers-idl
|
||||
partial interface mixin GlobalEventHandlers {
|
||||
attribute EventHandler ontransitionrun;
|
||||
|
|
|
@ -1582,11 +1582,11 @@ impl Window {
|
|||
}
|
||||
|
||||
let for_display = reflow_goal == ReflowGoal::Full;
|
||||
let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
|
||||
if for_display && self.suppress_reflow.get() {
|
||||
debug!(
|
||||
"Suppressing reflow pipeline {} for reason {:?} before FirstLoad or RefreshTick",
|
||||
self.upcast::<GlobalScope>().pipeline_id(),
|
||||
reason
|
||||
pipeline_id, reason
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
@ -1617,11 +1617,7 @@ impl Window {
|
|||
|
||||
// On debug mode, print the reflow event information.
|
||||
if self.relayout_event {
|
||||
debug_reflow_events(
|
||||
self.upcast::<GlobalScope>().pipeline_id(),
|
||||
&reflow_goal,
|
||||
&reason,
|
||||
);
|
||||
debug_reflow_events(pipeline_id, &reflow_goal, &reason);
|
||||
}
|
||||
|
||||
let document = self.Document();
|
||||
|
@ -1699,12 +1695,11 @@ impl Window {
|
|||
{
|
||||
let (responder, responder_listener) =
|
||||
ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap();
|
||||
let pipeline = self.upcast::<GlobalScope>().pipeline_id();
|
||||
let image_cache_chan = self.image_cache_chan.clone();
|
||||
ROUTER.add_route(
|
||||
responder_listener.to_opaque(),
|
||||
Box::new(move |message| {
|
||||
let _ = image_cache_chan.send((pipeline, message.to().unwrap()));
|
||||
let _ = image_cache_chan.send((pipeline_id, message.to().unwrap()));
|
||||
}),
|
||||
);
|
||||
self.image_cache
|
||||
|
@ -1714,7 +1709,7 @@ impl Window {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
ScriptThread::note_newly_transitioning_nodes(complete.newly_transitioning_nodes);
|
||||
ScriptThread::note_newly_animating_nodes(pipeline_id, complete.newly_animating_nodes);
|
||||
}
|
||||
|
||||
true
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
|
||||
use crate::devtools;
|
||||
use crate::document_loader::DocumentLoader;
|
||||
use crate::dom::animationevent::AnimationEvent;
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::AnimationEventBinding::AnimationEventInit;
|
||||
use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
|
||||
DocumentMethods, DocumentReadyState,
|
||||
};
|
||||
|
@ -138,9 +140,9 @@ use script_traits::{
|
|||
EventResult, HistoryEntryReplacement, InitialScriptState, JsEvalResult, LayoutMsg, LoadData,
|
||||
LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType, NewLayoutInfo, Painter,
|
||||
ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory, ScriptToConstellationChan,
|
||||
StructuredSerializedData, TimerSchedulerMsg, TouchEventType, TouchId, TransitionEventType,
|
||||
UntrustedNodeAddress, UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta, WindowSizeData,
|
||||
WindowSizeType,
|
||||
StructuredSerializedData, TimerSchedulerMsg, TouchEventType, TouchId,
|
||||
TransitionOrAnimationEventType, UntrustedNodeAddress, UpdatePipelineIdReason,
|
||||
WebrenderIpcSender, WheelDelta, WindowSizeData, WindowSizeType,
|
||||
};
|
||||
use servo_atoms::Atom;
|
||||
use servo_config::opts;
|
||||
|
@ -639,7 +641,7 @@ pub struct ScriptThread {
|
|||
|
||||
/// A list of nodes with in-progress CSS transitions, which roots them for the duration
|
||||
/// of the transition.
|
||||
transitioning_nodes: DomRefCell<Vec<Dom<Node>>>,
|
||||
animating_nodes: DomRefCell<HashMap<PipelineId, Vec<Dom<Node>>>>,
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#custom-element-reactions-stack>
|
||||
custom_element_reaction_stack: CustomElementReactionStack,
|
||||
|
@ -823,7 +825,10 @@ impl ScriptThread {
|
|||
})
|
||||
}
|
||||
|
||||
pub unsafe fn note_newly_transitioning_nodes(nodes: Vec<UntrustedNodeAddress>) {
|
||||
pub unsafe fn note_newly_animating_nodes(
|
||||
pipeline_id: PipelineId,
|
||||
nodes: Vec<UntrustedNodeAddress>,
|
||||
) {
|
||||
SCRIPT_THREAD_ROOT.with(|root| {
|
||||
let script_thread = &*root.get().unwrap();
|
||||
let js_runtime = script_thread.js_runtime.rt();
|
||||
|
@ -831,8 +836,10 @@ impl ScriptThread {
|
|||
.into_iter()
|
||||
.map(|n| Dom::from_ref(&*from_untrusted_node_address(js_runtime, n)));
|
||||
script_thread
|
||||
.transitioning_nodes
|
||||
.animating_nodes
|
||||
.borrow_mut()
|
||||
.entry(pipeline_id)
|
||||
.or_insert_with(Vec::new)
|
||||
.extend(new_nodes);
|
||||
})
|
||||
}
|
||||
|
@ -1345,7 +1352,7 @@ impl ScriptThread {
|
|||
|
||||
docs_with_no_blocking_loads: Default::default(),
|
||||
|
||||
transitioning_nodes: Default::default(),
|
||||
animating_nodes: Default::default(),
|
||||
|
||||
custom_element_reaction_stack: CustomElementReactionStack::new(),
|
||||
|
||||
|
@ -1697,7 +1704,7 @@ impl ScriptThread {
|
|||
FocusIFrame(id, ..) => Some(id),
|
||||
WebDriverScriptCommand(id, ..) => Some(id),
|
||||
TickAllAnimations(id) => Some(id),
|
||||
TransitionEvent { .. } => None,
|
||||
TransitionOrAnimationEvent { .. } => None,
|
||||
WebFontLoaded(id) => Some(id),
|
||||
DispatchIFrameLoadEvent {
|
||||
target: _,
|
||||
|
@ -1898,18 +1905,18 @@ impl ScriptThread {
|
|||
ConstellationControlMsg::TickAllAnimations(pipeline_id) => {
|
||||
self.handle_tick_all_animations(pipeline_id)
|
||||
},
|
||||
ConstellationControlMsg::TransitionEvent {
|
||||
ConstellationControlMsg::TransitionOrAnimationEvent {
|
||||
pipeline_id,
|
||||
event_type,
|
||||
node,
|
||||
property_name,
|
||||
property_or_animation_name,
|
||||
elapsed_time,
|
||||
} => {
|
||||
self.handle_transition_event(
|
||||
self.handle_transition_or_animation_event(
|
||||
pipeline_id,
|
||||
event_type,
|
||||
node,
|
||||
property_name,
|
||||
property_or_animation_name,
|
||||
elapsed_time,
|
||||
);
|
||||
},
|
||||
|
@ -2846,6 +2853,9 @@ impl ScriptThread {
|
|||
.send((id, ScriptMsg::PipelineExited))
|
||||
.ok();
|
||||
|
||||
// Remove any rooted nodes for active animations and transitions.
|
||||
self.animating_nodes.borrow_mut().remove(&id);
|
||||
|
||||
// Now that layout is shut down, it's OK to remove the document.
|
||||
if let Some(document) = document {
|
||||
// We don't want to dispatch `mouseout` event pointing to non-existing element
|
||||
|
@ -2916,20 +2926,32 @@ impl ScriptThread {
|
|||
/// Handles firing of transition-related events.
|
||||
///
|
||||
/// TODO(mrobinson): Add support for more events.
|
||||
fn handle_transition_event(
|
||||
fn handle_transition_or_animation_event(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
event_type: TransitionEventType,
|
||||
event_type: TransitionOrAnimationEventType,
|
||||
unsafe_node: UntrustedNodeAddress,
|
||||
property_name: String,
|
||||
property_or_animation_name: String,
|
||||
elapsed_time: f64,
|
||||
) {
|
||||
let js_runtime = self.js_runtime.rt();
|
||||
let node = unsafe { from_untrusted_node_address(js_runtime, unsafe_node) };
|
||||
|
||||
let node_index = self
|
||||
.transitioning_nodes
|
||||
.borrow()
|
||||
// We limit the scope of the borrow here, so that we don't maintain this borrow
|
||||
// and then incidentally trigger another layout. That might result in a double
|
||||
// mutable borrow of `animating_nodes`.
|
||||
{
|
||||
let mut animating_nodes = self.animating_nodes.borrow_mut();
|
||||
let nodes = match animating_nodes.get_mut(&pipeline_id) {
|
||||
Some(nodes) => nodes,
|
||||
None => {
|
||||
return warn!(
|
||||
"Ignoring transition event for pipeline without animating nodes."
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
let node_index = nodes
|
||||
.iter()
|
||||
.position(|n| &**n as *const _ == &*node as *const _);
|
||||
let node_index = match node_index {
|
||||
|
@ -2937,45 +2959,58 @@ impl ScriptThread {
|
|||
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.");
|
||||
warn!("Ignoring transition event for unknown node.");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
if self.closed_pipelines.borrow().contains(&pipeline_id) {
|
||||
warn!("Ignoring transition event for closed pipeline.");
|
||||
return;
|
||||
if event_type.finalizes_transition_or_animation() {
|
||||
nodes.remove(node_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Not quite the right thing - see #13865.
|
||||
if event_type.finalizes_transition_or_animation() {
|
||||
node.dirty(NodeDamage::NodeStyleDamaged);
|
||||
}
|
||||
|
||||
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 => {
|
||||
self.transitioning_nodes.borrow_mut().remove(node_index);
|
||||
atom!("transitioncancel")
|
||||
},
|
||||
TransitionOrAnimationEventType::AnimationEnd => atom!("animationend"),
|
||||
TransitionOrAnimationEventType::TransitionCancel => atom!("transitioncancel"),
|
||||
TransitionOrAnimationEventType::TransitionEnd => atom!("transitionend"),
|
||||
TransitionOrAnimationEventType::TransitionRun => atom!("transitionrun"),
|
||||
};
|
||||
|
||||
let event_init = TransitionEventInit {
|
||||
parent: EventInit {
|
||||
let parent = EventInit {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
},
|
||||
propertyName: DOMString::from(property_name),
|
||||
elapsedTime: Finite::new(elapsed_time as f32).unwrap(),
|
||||
// TODO: Handle pseudo-elements properly
|
||||
pseudoElement: DOMString::new(),
|
||||
};
|
||||
|
||||
// TODO: Handle pseudo-elements properly
|
||||
let property_or_animation_name = DOMString::from(property_or_animation_name);
|
||||
let elapsed_time = Finite::new(elapsed_time as f32).unwrap();
|
||||
let window = window_from_node(&*node);
|
||||
|
||||
if event_type.is_transition_event() {
|
||||
let event_init = TransitionEventInit {
|
||||
parent,
|
||||
propertyName: property_or_animation_name,
|
||||
elapsedTime: elapsed_time,
|
||||
pseudoElement: DOMString::new(),
|
||||
};
|
||||
TransitionEvent::new(&window, event_atom, &event_init)
|
||||
.upcast::<Event>()
|
||||
.fire(node.upcast());
|
||||
} else {
|
||||
let event_init = AnimationEventInit {
|
||||
parent,
|
||||
animationName: property_or_animation_name,
|
||||
elapsedTime: elapsed_time,
|
||||
pseudoElement: DOMString::new(),
|
||||
};
|
||||
AnimationEvent::new(&window, event_atom, &event_init)
|
||||
.upcast::<Event>()
|
||||
.fire(node.upcast());
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles a Web font being loaded. Does nothing if the page no longer exists.
|
||||
|
|
|
@ -193,7 +193,7 @@ pub struct ReflowComplete {
|
|||
/// The list of images that were encountered that are in progress.
|
||||
pub pending_images: Vec<PendingImage>,
|
||||
/// The list of nodes that initiated a CSS transition.
|
||||
pub newly_transitioning_nodes: Vec<UntrustedNodeAddress>,
|
||||
pub newly_animating_nodes: Vec<UntrustedNodeAddress>,
|
||||
}
|
||||
|
||||
/// Information needed for a script-initiated reflow.
|
||||
|
|
|
@ -282,16 +282,40 @@ pub enum UpdatePipelineIdReason {
|
|||
Traversal,
|
||||
}
|
||||
|
||||
/// The type of transition event to trigger.
|
||||
/// 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 TransitionEventType {
|
||||
/// The transition has started running.
|
||||
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 transition has ended by reaching the end of its animation.
|
||||
/// "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 transition ended early for some reason, such as the property
|
||||
/// no longer being transitionable or being replaced by another transition.
|
||||
/// "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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages sent from the constellation or layout to the script thread.
|
||||
|
@ -380,16 +404,17 @@ pub enum ConstellationControlMsg {
|
|||
WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
|
||||
/// Notifies script thread that all animations are done
|
||||
TickAllAnimations(PipelineId),
|
||||
/// Notifies the script thread that a transition related event should be sent.
|
||||
TransitionEvent {
|
||||
/// Notifies the script thread that a transition or animation related event should be sent.
|
||||
TransitionOrAnimationEvent {
|
||||
/// The pipeline id of the layout task that sent this message.
|
||||
pipeline_id: PipelineId,
|
||||
/// The type of transition event this should trigger.
|
||||
event_type: TransitionEventType,
|
||||
event_type: TransitionOrAnimationEventType,
|
||||
/// The address of the node which owns this transition.
|
||||
node: UntrustedNodeAddress,
|
||||
/// The property name of the property that is transitioning.
|
||||
property_name: String,
|
||||
/// 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).
|
||||
property_or_animation_name: String,
|
||||
/// The elapsed time property to send with this transition event.
|
||||
elapsed_time: f64,
|
||||
},
|
||||
|
@ -452,7 +477,7 @@ impl fmt::Debug for ConstellationControlMsg {
|
|||
FocusIFrame(..) => "FocusIFrame",
|
||||
WebDriverScriptCommand(..) => "WebDriverScriptCommand",
|
||||
TickAllAnimations(..) => "TickAllAnimations",
|
||||
TransitionEvent { .. } => "TransitionEvent",
|
||||
TransitionOrAnimationEvent { .. } => "TransitionOrAnimationEvent",
|
||||
WebFontLoaded(..) => "WebFontLoaded",
|
||||
DispatchIFrameLoadEvent { .. } => "DispatchIFrameLoadEvent",
|
||||
DispatchStorageEvent(..) => "DispatchStorageEvent",
|
||||
|
|
|
@ -172,6 +172,17 @@ impl KeyframesAnimationState {
|
|||
self.current_direction = old_direction;
|
||||
self.started_at = new_started_at;
|
||||
}
|
||||
|
||||
/// Calculate the active-duration of this animation according to
|
||||
/// https://drafts.csswg.org/css-animations/#active-duration. active-duration
|
||||
/// is not really meaningful for infinite animations so we just return 0
|
||||
/// here in that case.
|
||||
pub fn active_duration(&self) -> f64 {
|
||||
match self.iteration_state {
|
||||
KeyframesIterationState::Finite(_, max) => self.duration * (max as f64),
|
||||
KeyframesIterationState::Infinite => 0.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for KeyframesAnimationState {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[Element-getAnimations.tentative.html]
|
||||
bug: https://github.com/servo/servo/issues/21564
|
||||
expected: TIMEOUT
|
||||
[getAnimations for CSS Animations with animation-name: none]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -35,7 +34,7 @@
|
|||
expected: FAIL
|
||||
|
||||
[getAnimations for CSS Animations that have finished but are forwards filling]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
[getAnimations returns objects with the same identity]
|
||||
expected: FAIL
|
||||
|
@ -56,7 +55,7 @@
|
|||
expected: FAIL
|
||||
|
||||
[getAnimations for CSS Animations that have finished]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
[{ subtree: false } on a leaf element returns the element's animations and ignore pseudo-elements]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
[animationevent-interface.html]
|
||||
[the event is an instance of AnimationEvent]
|
||||
expected: FAIL
|
||||
|
||||
[animationName is readonly]
|
||||
expected: FAIL
|
||||
|
||||
[type argument is null]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime has default value of 0.0]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime is readonly]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to 0.5]
|
||||
expected: FAIL
|
||||
|
||||
[animationEventInit argument is empty dictionary]
|
||||
expected: FAIL
|
||||
|
||||
[event type set to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[Missing type argument]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent.pseudoElement initialized from the dictionary]
|
||||
expected: FAIL
|
||||
|
||||
[the event inherts from Event]
|
||||
expected: FAIL
|
||||
|
||||
[type argument is string]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to 'sample']
|
||||
expected: FAIL
|
||||
|
||||
[animationEventInit argument is undefined]
|
||||
expected: FAIL
|
||||
|
||||
[animationEventInit argument is null]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEventInit properties set value]
|
||||
expected: FAIL
|
||||
|
||||
[animationName has default value of empty string]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to -0.5]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime cannot be set to -Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime cannot be set to Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to [\]]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to null]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to an object with a valueOf function]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to an object with a valueOf function]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime cannot be set to NaN]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to true]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to '']
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to [0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to true]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to false]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime set to [\]]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime cannot be set to [0.5, 1.0\]]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to null]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to an object]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime cannot be set to 'sample']
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to [1, 2, 3\]]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to false]
|
||||
expected: FAIL
|
||||
|
||||
[animationName set to a number]
|
||||
expected: FAIL
|
||||
|
||||
[elapsedTime cannot be set to an object]
|
||||
expected: FAIL
|
||||
|
|
@ -4,9 +4,6 @@
|
|||
[animationiteration event is instanceof AnimationEvent]
|
||||
expected: TIMEOUT
|
||||
|
||||
[animationend event is instanceof AnimationEvent]
|
||||
expected: TIMEOUT
|
||||
|
||||
[animationstart event is instanceof AnimationEvent]
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,61 +1,25 @@
|
|||
[idlharness.html]
|
||||
[AnimationEvent interface: attribute pseudoElement]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: attribute onanimationend]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: attribute onanimationiteration]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[CSSKeyframeRule interface: attribute style]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: attribute elapsedTime]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: attribute onanimationstart]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLElement interface: attribute onanimationiteration]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent must be primary interface of new AnimationEvent("animationstart")]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: attribute onanimationcancel]
|
||||
expected: FAIL
|
||||
|
||||
[CSSKeyframeRule interface: attribute keyText]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: new AnimationEvent("animationstart") must inherit property "pseudoElement" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Stringification of new AnimationEvent("animationstart")]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: attribute onanimationend]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLElement interface: attribute onanimationstart]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: new AnimationEvent("animationstart") must inherit property "animationName" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: attribute onanimationcancel]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -65,24 +29,9 @@
|
|||
[HTMLElement interface: attribute onanimationcancel]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: attribute animationName]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: attribute onanimationiteration]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: new AnimationEvent("animationstart") must inherit property "elapsedTime" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[CSSKeyframeRule interface: keyframes.cssRules[0\] must inherit property "keyText" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[AnimationEvent interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLElement interface: attribute onanimationend]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
[variable-animation-substitute-into-keyframe-shorthand.html]
|
||||
bug: https://github.com/servo/servo/issues/21564
|
||||
expected: TIMEOUT
|
||||
[Verify border-bottom-color before animation]
|
||||
expected: FAIL
|
||||
|
||||
[Verify border-bottom-color after animation]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
[variable-animation-substitute-into-keyframe-transform.html]
|
||||
bug: https://github.com/servo/servo/issues/21564
|
||||
expected: TIMEOUT
|
||||
[Verify transform before animation]
|
||||
expected: FAIL
|
||||
|
||||
[Verify transform after animation]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
[variable-animation-substitute-into-keyframe.html]
|
||||
bug: https://github.com/servo/servo/issues/21564
|
||||
expected: TIMEOUT
|
||||
[Verify color before animation]
|
||||
expected: FAIL
|
||||
|
||||
[Verify color after animation]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
[variable-animation-substitute-within-keyframe-fallback.html]
|
||||
bug: https://github.com/servo/servo/issues/21564
|
||||
expected: TIMEOUT
|
||||
[Verify color before animation]
|
||||
expected: FAIL
|
||||
|
||||
[Verify color after animation]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
[variable-animation-substitute-within-keyframe-multiple.html]
|
||||
bug: https://github.com/servo/servo/issues/21564
|
||||
expected: TIMEOUT
|
||||
[Verify color before animation]
|
||||
expected: FAIL
|
||||
|
||||
[Verify color after animation]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
[variable-animation-substitute-within-keyframe.html]
|
||||
bug: https://github.com/servo/servo/issues/21564
|
||||
expected: TIMEOUT
|
||||
[Verify color before animation]
|
||||
expected: FAIL
|
||||
|
||||
[Verify color after animation]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[webkit-animation-end-event.html]
|
||||
expected: TIMEOUT
|
||||
[event types for prefixed and unprefixed animationend event handlers should be named appropriately]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -6,13 +7,13 @@
|
|||
expected: FAIL
|
||||
|
||||
[webkitAnimationEnd event listener should not trigger if an unprefixed event handler also exists]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[webkitAnimationEnd event listener is case sensitive]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[webkitAnimationEnd event listener should trigger for an animation]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[onwebkitanimationend event handler should trigger for an animation]
|
||||
expected: FAIL
|
||||
|
@ -20,20 +21,17 @@
|
|||
[onanimationend and onwebkitanimationend are not aliases]
|
||||
expected: FAIL
|
||||
|
||||
[dispatchEvent of a webkitAnimationEnd event does not trigger an unprefixed event handler or listener]
|
||||
expected: FAIL
|
||||
|
||||
[dispatchEvent of a webkitAnimationEnd event does trigger a prefixed event handler or listener]
|
||||
expected: FAIL
|
||||
|
||||
[webkitAnimationEnd event listener should not trigger if an unprefixed listener also exists]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[dispatchEvent of an animationend event does not trigger a prefixed event handler or listener]
|
||||
expected: FAIL
|
||||
|
||||
[event types for prefixed and unprefixed animationend event listeners should be named appropriately]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[onwebkitanimationend event handler should not trigger if an unprefixed event handler also exists]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
[webkit-transition-end-event.html]
|
||||
[dispatchEvent of a webkitTransitionEnd event does not trigger an unprefixed event handler or listener]
|
||||
expected: FAIL
|
||||
|
||||
[dispatchEvent of an transitionend event does not trigger a prefixed event handler or listener]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -13870,7 +13870,7 @@
|
|||
]
|
||||
],
|
||||
"interfaces.html": [
|
||||
"1a579837cc22d31a7792566615d9e321b3d7fe39",
|
||||
"b6034be26af3c2edd1ef41703857fa99bd2cd639",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// IMPORTANT: Do not change the list below without review from a DOM peer!
|
||||
test_interfaces([
|
||||
"AnalyserNode",
|
||||
"AnimationEvent",
|
||||
"Attr",
|
||||
"Audio",
|
||||
"AudioBuffer",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue