avm2: Implement `dispatchEvent`.

Event dispatch is a surprisingly complicated procedure, so this makes sure to test:

1. Event dispatch on bare dispatchers
2. Event dispatch on hierarchial dispatchers (movieclips)
3. Event cancellation (which is reported by `dispatchEvent`)
4. The implicit `this` on unbound event handlers. I'm not yet sure if this is a special property of event dispatch or if all unbound functions inherit their global scope as `this`.
5. The execution order of handlers on both bare and hierarchial dispatchers
6. Delegation to hierarchial dispatchers
7. Modification of the dispatch list during dispatch of an event. Surprisingly enough, you can add handlers to the event you're handling and expect them to execute, *if* you added them to a further object in the order *or* you added a bubble handler in the capture phase.
This commit is contained in:
David Wendt 2020-12-19 13:50:45 -05:00 committed by Mike Welsh
parent f33e5f893a
commit 19219ad0c1
34 changed files with 2167 additions and 0 deletions

View File

@ -155,13 +155,25 @@ impl<'gc> Event<'gc> {
self.event_phase self.event_phase
} }
pub fn set_phase(&mut self, phase: EventPhase) {
self.event_phase = phase;
}
pub fn target(&self) -> Option<Object<'gc>> { pub fn target(&self) -> Option<Object<'gc>> {
self.target self.target
} }
pub fn set_target(&mut self, target: Object<'gc>) {
self.target = Some(target)
}
pub fn current_target(&self) -> Option<Object<'gc>> { pub fn current_target(&self) -> Option<Object<'gc>> {
self.current_target self.current_target
} }
pub fn set_current_target(&mut self, current_target: Object<'gc>) {
self.current_target = Some(current_target)
}
} }
/// A set of handlers organized by event type, priority, and order added. /// A set of handlers organized by event type, priority, and order added.
@ -267,6 +279,27 @@ impl<'gc> DispatchList<'gc> {
false false
} }
/// Yield the event handlers on this dispatch list for a given event.
///
/// Event handlers will be yielded in the order they are intended to be
/// executed.
///
/// `use_capture` indicates if you want handlers that execute during the
/// capture phase, or handlers that execute during the bubble and target
/// phases.
pub fn iter_event_handlers<'a>(
&'a mut self,
event: impl Into<AvmString<'gc>>,
use_capture: bool,
) -> impl 'a + Iterator<Item = Object<'gc>> {
self.get_event_mut(event)
.iter()
.rev()
.flat_map(|(_p, v)| v.iter())
.filter(move |eh| eh.use_capture == use_capture)
.map(|eh| eh.handler)
}
} }
/// A single instance of an event handler. /// A single instance of an event handler.

View File

@ -2,6 +2,7 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::class::Class; use crate::avm2::class::Class;
use crate::avm2::events::EventPhase;
use crate::avm2::method::Method; use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{DispatchObject, Object, TObject}; use crate::avm2::object::{DispatchObject, Object, TObject};
@ -233,6 +234,141 @@ pub fn will_trigger<'gc>(
Ok(false.into()) Ok(false.into())
} }
/// Call all of the event handlers on a given target.
///
/// The `target` is the current target of the `event`. `event` must be a valid
/// `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
/// call the wrong handlers.
pub fn dispatch_event_to_target<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
mut target: Object<'gc>,
event: Object<'gc>,
) -> Result<(), Error> {
let dispatch_list = target
.get_property(
target,
&QName::new(
Namespace::ruffle_private("EventDispatcher"),
"dispatch_list",
),
activation,
)?
.coerce_to_object(activation)?;
let mut evtmut = event.as_event_mut(activation.context.gc_context).unwrap();
let name = evtmut.event_type();
let use_capture = evtmut.phase() == EventPhase::Capturing;
evtmut.set_current_target(target);
drop(evtmut);
let handlers: Vec<Object<'gc>> = dispatch_list
.as_dispatch_mut(activation.context.gc_context)
.ok_or_else(|| Error::from("Internal dispatch list is missing during dispatch!"))?
.iter_event_handlers(name, use_capture)
.collect();
for handler in handlers.iter() {
if event
.as_event()
.unwrap()
.is_propagation_stopped_immediately()
{
break;
}
handler.call(
activation.global_scope().coerce_to_object(activation).ok(),
&[event.into()],
activation,
None,
)?;
}
Ok(())
}
/// Implements `EventDispatcher.dispatchEvent`.
pub fn dispatch_event<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
let event = args
.get(0)
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_object(activation)?;
if event.as_event().is_none() {
return Err("Dispatched Events must be subclasses of Event.".into());
}
if let Some(mut this) = this {
let target = this
.get_property(
this,
&QName::new(Namespace::ruffle_private("EventDispatcher"), "target"),
activation,
)?
.coerce_to_object(activation)
.ok()
.unwrap_or(this);
let mut ancestor_list = Vec::new();
let mut parent = parent_of(target);
while let Some(par) = parent {
ancestor_list.push(par);
parent = parent_of(par);
}
let mut evtmut = event.as_event_mut(activation.context.gc_context).unwrap();
evtmut.set_phase(EventPhase::Capturing);
evtmut.set_target(target);
drop(evtmut);
for ancestor in ancestor_list.iter().rev() {
if event.as_event().unwrap().is_propagation_stopped() {
break;
}
dispatch_event_to_target(activation, *ancestor, event)?;
}
event
.as_event_mut(activation.context.gc_context)
.unwrap()
.set_phase(EventPhase::AtTarget);
if !event.as_event().unwrap().is_propagation_stopped() {
dispatch_event_to_target(activation, target, event)?;
}
event
.as_event_mut(activation.context.gc_context)
.unwrap()
.set_phase(EventPhase::Bubbling);
if event.as_event().unwrap().is_bubbling() {
for ancestor in ancestor_list.iter() {
if event.as_event().unwrap().is_propagation_stopped() {
break;
}
dispatch_event_to_target(activation, *ancestor, event)?;
}
}
}
let was_not_cancelled = !event.as_event().unwrap().is_cancelled();
Ok(was_not_cancelled.into())
}
/// Implements `flash.events.EventDispatcher`'s class constructor. /// Implements `flash.events.EventDispatcher`'s class constructor.
pub fn class_init<'gc>( pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
@ -272,6 +408,10 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
QName::new(Namespace::public_namespace(), "willTrigger"), QName::new(Namespace::public_namespace(), "willTrigger"),
Method::from_builtin(will_trigger), Method::from_builtin(will_trigger),
)); ));
write.define_instance_trait(Trait::from_method(
QName::new(Namespace::public_namespace(), "dispatchEvent"),
Method::from_builtin(dispatch_event),
));
write.define_instance_trait(Trait::from_slot( write.define_instance_trait(Trait::from_slot(
QName::new(Namespace::ruffle_private("EventDispatcher"), "target"), QName::new(Namespace::ruffle_private("EventDispatcher"), "target"),

View File

@ -462,6 +462,15 @@ swf_tests! {
(as3_eventdispatcher_haseventlistener, "avm2/eventdispatcher_haseventlistener", 1), (as3_eventdispatcher_haseventlistener, "avm2/eventdispatcher_haseventlistener", 1),
(as3_eventdispatcher_willtrigger, "avm2/eventdispatcher_willtrigger", 1), (as3_eventdispatcher_willtrigger, "avm2/eventdispatcher_willtrigger", 1),
(as3_movieclip_willtrigger, "avm2/movieclip_willtrigger", 3), (as3_movieclip_willtrigger, "avm2/movieclip_willtrigger", 3),
(as3_eventdispatcher_dispatchevent, "avm2/eventdispatcher_dispatchevent", 1),
(as3_eventdispatcher_dispatchevent_handlerorder, "avm2/eventdispatcher_dispatchevent_handlerorder", 1),
(as3_eventdispatcher_dispatchevent_cancel, "avm2/eventdispatcher_dispatchevent_cancel", 1),
(as3_eventdispatcher_dispatchevent_this, "avm2/eventdispatcher_dispatchevent_this", 1),
(as3_movieclip_dispatchevent, "avm2/movieclip_dispatchevent", 1),
(as3_movieclip_dispatchevent_handlerorder, "avm2/movieclip_dispatchevent_handlerorder", 1),
(as3_movieclip_dispatchevent_cancel, "avm2/movieclip_dispatchevent_cancel", 1),
(as3_movieclip_dispatchevent_target, "avm2/movieclip_dispatchevent_target", 1),
(as3_movieclip_dispatchevent_selfadd, "avm2/movieclip_dispatchevent_selfadd", 1),
} }
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.

View File

@ -0,0 +1,32 @@
package {
public class Test {
}
}
import flash.events.Event;
import flash.events.EventDispatcher;
function introspect_event(event: Event) {
trace("//(Handled an event...)");
trace("//event.type");
trace(event.type);
trace("//event.eventPhase");
trace(event.eventPhase);
trace("//event.target === evtd");
trace(event.target === evtd);
trace("//event.currentTarget === evtd");
trace(event.currentTarget === evtd);
}
trace("//var evtd = new EventDispatcher();");
var evtd = new EventDispatcher();
trace("//evtd.addEventListener('test', introspect_event, false, 0);");
evtd.addEventListener('test', introspect_event, false, 0);
trace("//evtd.dispatchEvent('test');");
evtd.dispatchEvent(new Event('test'));

View File

@ -0,0 +1,12 @@
//var evtd = new EventDispatcher();
//evtd.addEventListener('test', introspect_event, false, 0);
//evtd.dispatchEvent('test');
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === evtd
true
//event.currentTarget === evtd
true

View File

@ -0,0 +1,56 @@
package {
public class Test {
}
}
import flash.events.Event;
import flash.events.EventDispatcher;
function nocancel_event(event: Event) {
trace("//(nocancel_event handler)");
}
function cancel_event(event: Event) {
trace("//(cancel_event handler)");
event.preventDefault();
}
function stop_event(event: Event) {
trace("//(stop_event handler)");
event.stopPropagation();
}
function stop_immediate_event(event: Event) {
trace("//(stop_immediate_event handler)");
event.stopImmediatePropagation();
}
trace("//var evtd = new EventDispatcher();");
var evtd = new EventDispatcher();
trace("//evtd.addEventListener('test', nocancel_event, false, 0);");
evtd.addEventListener('test', nocancel_event, false, 0);
trace("//evtd.dispatchEvent('test');");
trace(evtd.dispatchEvent(new Event('test')));
trace("//evtd.addEventListener('test', cancel_event, false, 0);");
evtd.addEventListener('test', cancel_event, false, 0);
trace("//evtd.dispatchEvent('test');");
trace(evtd.dispatchEvent(new Event('test')));
trace("//evtd.removeEventListener('test', cancel_event);");
evtd.removeEventListener('test', cancel_event);
trace("//evtd.addEventListener('test', stop_event, false, 5);");
evtd.addEventListener('test', stop_event, false, 5);
trace("//evtd.dispatchEvent('test');");
trace(evtd.dispatchEvent(new Event('test')));
trace("//evtd.addEventListener('test', stop_immediate_event, false, 10);");
evtd.addEventListener('test', stop_immediate_event, false, 10);
trace("//evtd.dispatchEvent('test');");
trace(evtd.dispatchEvent(new Event('test')));

View File

@ -0,0 +1,20 @@
//var evtd = new EventDispatcher();
//evtd.addEventListener('test', nocancel_event, false, 0);
//evtd.dispatchEvent('test');
//(nocancel_event handler)
true
//evtd.addEventListener('test', cancel_event, false, 0);
//evtd.dispatchEvent('test');
//(nocancel_event handler)
//(cancel_event handler)
true
//evtd.removeEventListener('test', cancel_event);
//evtd.addEventListener('test', stop_event, false, 5);
//evtd.dispatchEvent('test');
//(stop_event handler)
//(nocancel_event handler)
true
//evtd.addEventListener('test', stop_immediate_event, false, 10);
//evtd.dispatchEvent('test');
//(stop_immediate_event handler)
true

View File

@ -0,0 +1,55 @@
package {
public class Test {
}
}
import flash.events.Event;
import flash.events.EventDispatcher;
function handler_one(event: Event) {
trace("//(handler_one executed...)");
}
function handler_two(event: Event) {
trace("//(handler_two executed...)");
}
function handler_three(event: Event) {
trace("//(handler_three executed...)");
}
trace("//var evtd = new EventDispatcher();");
var evtd = new EventDispatcher();
trace("//evtd.addEventListener('test', handler_one, false, 0);");
evtd.addEventListener('test', handler_one, false, 0);
trace("//evtd.addEventListener('test', handler_two, false, 5);");
evtd.addEventListener('test', handler_two, false, 5);
trace("//evtd.addEventListener('test', handler_three, false, 0);");
evtd.addEventListener('test', handler_three, false, 0);
trace("//evtd.dispatchEvent('test');");
evtd.dispatchEvent(new Event('test'));
trace("//evtd.removeEventListener('test', handler_two);");
evtd.removeEventListener('test', handler_two);
trace("//evtd.dispatchEvent('test');");
evtd.dispatchEvent(new Event('test'));
trace("//evtd.addEventListener('test', handler_two, true, 5);");
evtd.addEventListener('test', handler_two, true, 5);
trace("//evtd.addEventListener('test2', handler_two, false, 5);");
evtd.addEventListener('test2', handler_two, false, 5);
trace("//evtd.dispatchEvent('test');");
evtd.dispatchEvent(new Event('test'));
trace("//evtd.addEventListener('test', handler_two, false, -5);");
evtd.addEventListener('test', handler_two, false, -5);
trace("//evtd.dispatchEvent('test');");
evtd.dispatchEvent(new Event('test'));

View File

@ -0,0 +1,22 @@
//var evtd = new EventDispatcher();
//evtd.addEventListener('test', handler_one, false, 0);
//evtd.addEventListener('test', handler_two, false, 5);
//evtd.addEventListener('test', handler_three, false, 0);
//evtd.dispatchEvent('test');
//(handler_two executed...)
//(handler_one executed...)
//(handler_three executed...)
//evtd.removeEventListener('test', handler_two);
//evtd.dispatchEvent('test');
//(handler_one executed...)
//(handler_three executed...)
//evtd.addEventListener('test', handler_two, true, 5);
//evtd.addEventListener('test2', handler_two, false, 5);
//evtd.dispatchEvent('test');
//(handler_one executed...)
//(handler_three executed...)
//evtd.addEventListener('test', handler_two, false, -5);
//evtd.dispatchEvent('test');
//(handler_one executed...)
//(handler_three executed...)
//(handler_two executed...)

View File

@ -0,0 +1,21 @@
package {
public class Test {
}
}
import flash.events.Event;
import flash.events.EventDispatcher;
function introspect_this(event: Event) {
trace("//this");
trace(this);
}
trace("//var evtd = new EventDispatcher();");
var evtd = new EventDispatcher();
trace("//evtd.addEventListener('test', introspect_this, false, 0);");
evtd.addEventListener('test', introspect_this, false, 0);
trace("//evtd.dispatchEvent('test');");
evtd.dispatchEvent(new Event('test'));

View File

@ -0,0 +1,5 @@
//var evtd = new EventDispatcher();
//evtd.addEventListener('test', introspect_this, false, 0);
//evtd.dispatchEvent('test');
//this
[object global]

View File

@ -0,0 +1,430 @@
//var s1 = this.symbol_1;
//var s2 = this.symbol_1.symbol_2;
//this.addEventListener('test', introspect_event, false, 0);
//this.addEventListener('test', introspect_event, true, 0);
//this.symbol_1.addEventListener('test', introspect_event, false, 0);
//this.symbol_1.addEventListener('test', introspect_event, true, 0);
//this.symbol_1.symbol_2.addEventListener('test', introspect_event, false, 0);
//this.symbol_1.symbol_2.addEventListener('test', introspect_event, true, 0);
//this.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
true
//event.target === s1
false
//event.target === s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//this.symbol_1.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//this.symbol_1.symbol_2.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//this.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
true
//event.target === s1
false
//event.target === s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//this.symbol_1.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//this.symbol_1.symbol_2.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
this.removeChild(s1);
//s1.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//s1.symbol_2.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//s1.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//s1.symbol_2.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
s1.removeChild(s2);
//s2.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//s2.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,102 @@
//var s1 = this.symbol_1;
//var s2 = this.symbol_1.symbol_2;
//this.addEventListener('test', nocancel_event, false, 0);
//this.addEventListener('test', cancel_event, false, 5);
//this.addEventListener('test', stop_event, false, 10);
//this.symbol_1.addEventListener('test', nocancel_event, false, 0);
//this.symbol_1.addEventListener('test', cancel_event, false, 5);
//this.symbol_1.addEventListener('test', stop_event, false, 10);
//this.symbol_1.symbol_2.addEventListener('test', nocancel_event, false, 0);
//this.symbol_1.symbol_2.addEventListener('test', cancel_event, false, 5);
//this.symbol_1.symbol_2.addEventListener('test', stop_event, false, 10);
//this.dispatchEvent(new Event('test'));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.symbol_1.dispatchEvent(new Event('test'));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.symbol_1.symbol_2.dispatchEvent(new Event('test'));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.dispatchEvent(new Event('test', true));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.symbol_1.dispatchEvent(new Event('test', true));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.symbol_1.symbol_2.dispatchEvent(new Event('test', true));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.addEventListener('test', nocancel_event, true, 0);
//this.addEventListener('test', cancel_event, true, 5);
//this.addEventListener('test', stop_event, true, 10);
//this.symbol_1.addEventListener('test', nocancel_event, true, 0);
//this.symbol_1.addEventListener('test', cancel_event, true, 5);
//this.symbol_1.addEventListener('test', stop_event, true, 10);
//this.symbol_1.symbol_2.addEventListener('test', nocancel_event, true, 0);
//this.symbol_1.symbol_2.addEventListener('test', cancel_event, true, 5);
//this.symbol_1.symbol_2.addEventListener('test', stop_event, true, 10);
//this.dispatchEvent(new Event('test'));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.symbol_1.dispatchEvent(new Event('test'));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.symbol_1.symbol_2.dispatchEvent(new Event('test'));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.dispatchEvent(new Event('test', true));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.symbol_1.dispatchEvent(new Event('test', true));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.symbol_1.symbol_2.dispatchEvent(new Event('test', true));
//(stop_event handler)
//(cancel_event handler)
//(nocancel_event handler)
true
//this.addEventListener('test', stop_immediate_event, false, 15);
//this.addEventListener('test', stop_immediate_event, true, 15);
//this.symbol_1.addEventListener('test', stop_immediate_event, false, 15);
//this.symbol_1.addEventListener('test', stop_immediate_event, true, 15);
//this.dispatchEvent(new Event('test'));
//(stop_immediate_event handler)
true
//this.symbol_1.dispatchEvent(new Event('test'));
//(stop_immediate_event handler)
true
//this.symbol_1.symbol_2.dispatchEvent(new Event('test'));
//(stop_immediate_event handler)
true
//this.dispatchEvent(new Event('test', true));
//(stop_immediate_event handler)
true
//this.symbol_1.dispatchEvent(new Event('test', true));
//(stop_immediate_event handler)
true
//this.symbol_1.symbol_2.dispatchEvent(new Event('test', true));
//(stop_immediate_event handler)
true

View File

@ -0,0 +1,251 @@
//var s1 = this.symbol_1;
//var s2 = this.symbol_1.symbol_2;
//this.addEventListener('test', handler_one, false, 0);
//this.addEventListener('test', handler_one, true, 0);
//this.addEventListener('test', handler_two, false, 5);
//this.addEventListener('test', handler_two, true, 5);
//this.addEventListener('test', handler_three, false, 0);
//this.addEventListener('test', handler_three, true, 0);
//this.symbol_1.addEventListener('test', handler_one, false, 0);
//this.symbol_1.addEventListener('test', handler_one, true, 0);
//this.symbol_1.addEventListener('test', handler_two, false, -5);
//this.symbol_1.addEventListener('test', handler_two, true, -5);
//this.symbol_1.addEventListener('test', handler_three, false, 0);
//this.symbol_1.addEventListener('test', handler_three, true, 0);
//this.symbol_1.symbol_2.addEventListener('test', handler_one, false, 0);
//this.symbol_1.symbol_2.addEventListener('test', handler_one, true, 0);
//this.symbol_1.symbol_2.addEventListener('test', handler_two, false, -5);
//this.symbol_1.symbol_2.addEventListener('test', handler_two, true, 5);
//this.symbol_1.symbol_2.addEventListener('test', handler_three, false, 0);
//this.symbol_1.symbol_2.addEventListener('test', handler_three, true, 0);
//this.dispatchEvent(new Event('test'));
//(handler_two called...)
//event.eventPhase;
2
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//this.symbol_1.dispatchEvent(new Event('test'));
//(handler_two called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
1
//(handler_three called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//this.symbol_1.symbol_2.dispatchEvent(new Event('test'));
//(handler_two called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
1
//(handler_three called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
1
//(handler_three called...)
//event.eventPhase;
1
//(handler_two called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//this.dispatchEvent(new Event('test', true));
//(handler_two called...)
//event.eventPhase;
2
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//this.symbol_1.dispatchEvent(new Event('test', true));
//(handler_two called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
1
//(handler_three called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
3
//(handler_one called...)
//event.eventPhase;
3
//(handler_three called...)
//event.eventPhase;
3
//this.symbol_1.symbol_2.dispatchEvent(new Event('test', true));
//(handler_two called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
1
//(handler_three called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
1
//(handler_three called...)
//event.eventPhase;
1
//(handler_two called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//(handler_one called...)
//event.eventPhase;
3
//(handler_three called...)
//event.eventPhase;
3
//(handler_two called...)
//event.eventPhase;
3
//(handler_two called...)
//event.eventPhase;
3
//(handler_one called...)
//event.eventPhase;
3
//(handler_three called...)
//event.eventPhase;
3
//this.symbol_1.symbol_2.removeEventListener('test', handler_three, true);
this.removeChild(s1);
//s1.dispatchEvent(new Event('test'));
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//s1.symbol_2.dispatchEvent(new Event('test'));
//(handler_one called...)
//event.eventPhase;
1
//(handler_three called...)
//event.eventPhase;
1
//(handler_two called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//s1.dispatchEvent(new Event('test', true));
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//s1.symbol_2.dispatchEvent(new Event('test', true));
//(handler_one called...)
//event.eventPhase;
1
//(handler_three called...)
//event.eventPhase;
1
//(handler_two called...)
//event.eventPhase;
1
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//(handler_one called...)
//event.eventPhase;
3
//(handler_three called...)
//event.eventPhase;
3
//(handler_two called...)
//event.eventPhase;
3
s1.removeChild(s2);
//s2.dispatchEvent(new Event('test'));
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2
//s2.dispatchEvent(new Event('test', true));
//(handler_one called...)
//event.eventPhase;
2
//(handler_three called...)
//event.eventPhase;
2
//(handler_two called...)
//event.eventPhase;
2

View File

@ -0,0 +1,80 @@
//var s1 = this.symbol_1;
//var s2 = this.symbol_1.symbol_2;
//this.addEventListener('test', selfadd_handler, true, 0);
//this.symbol_1.addEventListener('test', selfadd_handler, true, 0);
//this.symbol_1.symbol_2.addEventListener('test', selfadd_handler, false, 0);
//this.symbol_1.symbol_2.dispatchEvent(new Event('test', true));
//(adding bubbling handler to current target...)
//(adding capturing handler to current target...)
//(adding bubbling handler to first child of current target...)
//(adding capturing handler to first child of current target...)
//event.eventPhase
1
//event.currentTarget === this
true
//event.currentTarget === this.symbol_1
false
//event.currentTarget === this.symbol_1.symbol_2
false
//(adding bubbling handler to parent of current target...)
//(adding capturing handler to parent of current target...)
//(adding bubbling handler to current target...)
//(adding capturing handler to current target...)
//(adding bubbling handler to first child of current target...)
//(adding capturing handler to first child of current target...)
//event.eventPhase
1
//event.currentTarget === this
false
//event.currentTarget === this.symbol_1
true
//event.currentTarget === this.symbol_1.symbol_2
false
//(added_handler called)
//event.eventPhase
1
//event.currentTarget === this
false
//event.currentTarget === this.symbol_1
true
//event.currentTarget === this.symbol_1.symbol_2
false
//(adding bubbling handler to parent of current target...)
//(adding capturing handler to parent of current target...)
//(adding bubbling handler to current target...)
//(adding capturing handler to current target...)
//event.eventPhase
2
//event.currentTarget === this
false
//event.currentTarget === this.symbol_1
false
//event.currentTarget === this.symbol_1.symbol_2
true
//(added_handler called)
//event.eventPhase
2
//event.currentTarget === this
false
//event.currentTarget === this.symbol_1
false
//event.currentTarget === this.symbol_1.symbol_2
true
//(added_handler called)
//event.eventPhase
3
//event.currentTarget === this
false
//event.currentTarget === this.symbol_1
true
//event.currentTarget === this.symbol_1.symbol_2
false
//(added_handler called)
//event.eventPhase
3
//event.currentTarget === this
true
//event.currentTarget === this.symbol_1
false
//event.currentTarget === this.symbol_1.symbol_2
false

View File

@ -0,0 +1,899 @@
//var s1 = this.symbol_1;
//var s2 = this.symbol_1.symbol_2;
//this.addEventListener('test', introspect_event, false, 0);
//this.addEventListener('test', introspect_event, true, 0);
//this.symbol_1.addEventListener('test', introspect_event, false, 0);
//this.symbol_1.addEventListener('test', introspect_event, true, 0);
//this.symbol_1.symbol_2.addEventListener('test', introspect_event, false, 0);
//this.symbol_1.symbol_2.addEventListener('test', introspect_event, true, 0);
//var evtd = new EventDispatcher(this);
//evtd.addEventListener('test', introspect_event, false, 0);
//evtd.addEventListener('test', introspect_event, true, 0);
//var evtd_s1 = new EventDispatcher(this.symbol_1);
//evtd_s1.addEventListener('test', introspect_event, false, 0);
//evtd_s1.addEventListener('test', introspect_event, true, 0);
//var evtd_s2 = new EventDispatcher(this.symbol_1.symbol_2);
//evtd_s2.addEventListener('test', introspect_event, false, 0);
//evtd_s2.addEventListener('test', introspect_event, true, 0);
//evtd.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
true
//event.target === s1
false
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//evtd_s1.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//evtd_s2.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//evtd.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
true
//event.target === s1
false
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//evtd_s1.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//evtd_s2.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//this.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
true
//event.target === s1
false
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//this.symbol_1.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//this.symbol_1.symbol_2.dispatchEvent(new Event('test'));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//this.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
true
//event.target === s1
false
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//this.symbol_1.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
true
//event.target === s2
false
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//this.symbol_1.symbol_2.dispatchEvent(new Event('test', true));
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
1
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
2
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
false
//event.currentTarget === s2
true
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
false
//event.currentTarget === s1
true
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false
//(Handled an event...)
//event.type
test
//event.eventPhase
3
//event.target === this
false
//event.target === s1
false
//event.target === s2
true
//event.target === evtd
false
//event.target === evtd_s1
false
//event.target === evtd_s2
false
//event.currentTarget === this
true
//event.currentTarget === s1
false
//event.currentTarget === s2
false
//event.currentTarget === evtd
false
//event.currentTarget === evtd_s1
false
//event.currentTarget === evtd_s2
false