diff options
Diffstat (limited to 'components/script/script_thread.rs')
-rw-r--r-- | components/script/script_thread.rs | 147 |
1 files changed, 91 insertions, 56 deletions
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 16fd866f1b9..165e526fa76 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -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,66 +2926,91 @@ 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() - .iter() - .position(|n| &**n as *const _ == &*node as *const _); - 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; - }, - }; + // 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." + ); + }, + }; - if self.closed_pipelines.borrow().contains(&pipeline_id) { - warn!("Ignoring transition event for closed pipeline."); - return; + let node_index = nodes + .iter() + .position(|n| &**n as *const _ == &*node as *const _); + 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 event for unknown node."); + 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 { - 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(), + let parent = EventInit { + bubbles: true, + cancelable: false, }; + // 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); - TransitionEvent::new(&window, event_atom, &event_init) - .upcast::<Event>() - .fire(node.upcast()); + + 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. |