avm2: Add an option to simulate event dispatch

This commit is contained in:
Kamil Jarosz 2024-07-07 13:04:13 +02:00 committed by TÖRÖK Attila
parent ebfff6aea1
commit 010310dd1b
3 changed files with 39 additions and 8 deletions

View File

@ -431,13 +431,36 @@ impl<'gc> Avm2<'gc> {
/// Dispatch an event on an object. /// Dispatch an event on an object.
/// ///
/// This will become its own self-contained activation and swallow /// This will become its own self-contained activation and swallow
/// any resulting resulting error (after logging). /// any resulting error (after logging).
/// ///
/// Attempts to dispatch a non-event object will panic. /// Attempts to dispatch a non-event object will panic.
pub fn dispatch_event( pub fn dispatch_event(
context: &mut UpdateContext<'_, 'gc>, context: &mut UpdateContext<'_, 'gc>,
event: Object<'gc>, event: Object<'gc>,
target: Object<'gc>, target: Object<'gc>,
) {
Self::dispatch_event_internal(context, event, target, false)
}
/// Simulate dispatching an event.
///
/// This method is similar to [`Self::dispatch_event`],
/// but it does not execute event handlers.
///
/// Returns `true` when the event would have been handled if not simulated.
pub fn simulate_event_dispatch(
context: &mut UpdateContext<'_, 'gc>,
event: Object<'gc>,
target: Object<'gc>,
) {
Self::dispatch_event_internal(context, event, target, true)
}
fn dispatch_event_internal(
context: &mut UpdateContext<'_, 'gc>,
event: Object<'gc>,
target: Object<'gc>,
simulate_dispatch: bool,
) { ) {
let event_name = event let event_name = event
.as_event() .as_event()
@ -445,7 +468,8 @@ impl<'gc> Avm2<'gc> {
.unwrap_or_else(|| panic!("cannot dispatch non-event object: {:?}", event)); .unwrap_or_else(|| panic!("cannot dispatch non-event object: {:?}", event));
let mut activation = Activation::from_nothing(context.reborrow()); let mut activation = Activation::from_nothing(context.reborrow());
if let Err(err) = events::dispatch_event(&mut activation, target, event) { if let Err(err) = events::dispatch_event(&mut activation, target, event, simulate_dispatch)
{
tracing::error!( tracing::error!(
"Encountered AVM2 error when dispatching `{}` event: {:?}", "Encountered AVM2 error when dispatching `{}` event: {:?}",
event_name, event_name,
@ -537,7 +561,8 @@ impl<'gc> Avm2<'gc> {
let mut activation = Activation::from_nothing(context.reborrow()); let mut activation = Activation::from_nothing(context.reborrow());
if object.is_of_type(on_type.inner_class_definition()) { if object.is_of_type(on_type.inner_class_definition()) {
if let Err(err) = events::dispatch_event(&mut activation, object, event) { if let Err(err) = events::dispatch_event(&mut activation, object, event, false)
{
tracing::error!( tracing::error!(
"Encountered AVM2 error when broadcasting `{}` event: {:?}", "Encountered AVM2 error when broadcasting `{}` event: {:?}",
event_name, event_name,

View File

@ -370,11 +370,12 @@ pub fn parent_of(target: Object<'_>) -> Option<Object<'_>> {
/// `EventObject`, or this function will panic. You must have already set the /// `EventObject`, or this function will panic. You must have already set the
/// event's phase to match what targets you are dispatching to, or you will /// event's phase to match what targets you are dispatching to, or you will
/// call the wrong handlers. /// call the wrong handlers.
pub fn dispatch_event_to_target<'gc>( fn dispatch_event_to_target<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
dispatcher: Object<'gc>, dispatcher: Object<'gc>,
target: Object<'gc>, target: Object<'gc>,
event: Object<'gc>, event: Object<'gc>,
simulate_dispatch: bool,
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
avm_debug!( avm_debug!(
activation.context.avm2, activation.context.avm2,
@ -413,6 +414,10 @@ pub fn dispatch_event_to_target<'gc>(
drop(evtmut); drop(evtmut);
if simulate_dispatch {
return Ok(());
}
for handler in handlers.iter() { for handler in handlers.iter() {
if event if event
.as_event() .as_event()
@ -441,6 +446,7 @@ pub fn dispatch_event<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
event: Object<'gc>, event: Object<'gc>,
simulate_dispatch: bool,
) -> Result<bool, Error<'gc>> { ) -> Result<bool, Error<'gc>> {
let target = this let target = this
.get_property( .get_property(
@ -486,7 +492,7 @@ pub fn dispatch_event<'gc>(
break; break;
} }
dispatch_event_to_target(activation, *ancestor, *ancestor, event)?; dispatch_event_to_target(activation, *ancestor, *ancestor, event, simulate_dispatch)?;
} }
event event
@ -495,7 +501,7 @@ pub fn dispatch_event<'gc>(
.set_phase(EventPhase::AtTarget); .set_phase(EventPhase::AtTarget);
if !event.as_event().unwrap().is_propagation_stopped() { if !event.as_event().unwrap().is_propagation_stopped() {
dispatch_event_to_target(activation, this, target, event)?; dispatch_event_to_target(activation, this, target, event, simulate_dispatch)?;
} }
event event
@ -509,7 +515,7 @@ pub fn dispatch_event<'gc>(
break; break;
} }
dispatch_event_to_target(activation, *ancestor, *ancestor, event)?; dispatch_event_to_target(activation, *ancestor, *ancestor, event, simulate_dispatch)?;
} }
} }

View File

@ -139,7 +139,7 @@ pub fn dispatch_event<'gc>(
return Err("Dispatched Events must be subclasses of Event.".into()); return Err("Dispatched Events must be subclasses of Event.".into());
} }
Ok(dispatch_event_internal(activation, this, event)?.into()) Ok(dispatch_event_internal(activation, this, event, false)?.into())
} }
/// Implements `EventDispatcher.toString`. /// Implements `EventDispatcher.toString`.