servo: Merge #15137 - Kill eventdispatcher (from nox:dispatch); r=jdm
authorAnthony Ramine <n.oxyde@gmail.com>
Sun, 22 Jan 2017 06:32:10 -0800
changeset 340626 53d07078a7c0b42cdc99d451a0d47abfa56ce226
parent 340625 43f1e925be4acd067f056e90e0076520ca243668
child 340627 fad6c9d357894eee05cd6cd11c904590ddc86e9f
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
servo: Merge #15137 - Kill eventdispatcher (from nox:dispatch); r=jdm Source-Repo: https://github.com/servo/servo Source-Revision: dc9706cf699dad0e0441b341b8d9d0b4ed7d7185
servo/components/script/dom/document.rs
servo/components/script/dom/event.rs
servo/components/script/dom/eventdispatcher.rs
servo/components/script/dom/eventtarget.rs
servo/components/script/dom/globalscope.rs
servo/components/script/dom/htmlscriptelement.rs
servo/components/script/dom/mod.rs
servo/components/script/dom/worker.rs
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -36,18 +36,17 @@ use dom::browsingcontext::BrowsingContex
 use dom::closeevent::CloseEvent;
 use dom::comment::Comment;
 use dom::customevent::CustomEvent;
 use dom::documentfragment::DocumentFragment;
 use dom::documenttype::DocumentType;
 use dom::domimplementation::DOMImplementation;
 use dom::element::{Element, ElementCreator, ElementPerformFullscreenEnter, ElementPerformFullscreenExit};
 use dom::errorevent::ErrorEvent;
-use dom::event::{Event, EventBubbles, EventCancelable, EventDefault};
-use dom::eventdispatcher::EventStatus;
+use dom::event::{Event, EventBubbles, EventCancelable, EventDefault, EventStatus};
 use dom::eventtarget::EventTarget;
 use dom::focusevent::FocusEvent;
 use dom::forcetouchevent::ForceTouchEvent;
 use dom::globalscope::GlobalScope;
 use dom::hashchangeevent::HashChangeEvent;
 use dom::htmlanchorelement::HTMLAnchorElement;
 use dom::htmlappletelement::HTMLAppletElement;
 use dom::htmlareaelement::HTMLAreaElement;
--- a/servo/components/script/dom/event.rs
+++ b/servo/components/script/dom/event.rs
@@ -1,109 +1,35 @@
 /* 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/. */
 
+use devtools_traits::{TimelineMarker, TimelineMarkerType};
+use dom::bindings::callback::ExceptionHandling;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::EventBinding;
 use dom::bindings::codegen::Bindings::EventBinding::{EventConstants, EventMethods};
 use dom::bindings::error::Fallible;
-use dom::bindings::js::{MutNullableJS, Root};
+use dom::bindings::inheritance::Castable;
+use dom::bindings::js::{JS, MutNullableJS, Root, RootedReference};
 use dom::bindings::refcounted::Trusted;
-use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
-use dom::eventdispatcher::EventStatus;
-use dom::eventtarget::EventTarget;
+use dom::document::Document;
+use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
 use dom::globalscope::GlobalScope;
+use dom::node::Node;
+use dom::virtualmethods::vtable_for;
+use dom::window::Window;
 use script_thread::Runnable;
 use servo_atoms::Atom;
 use std::cell::Cell;
 use std::default::Default;
 use time;
 
-#[derive(JSTraceable, Copy, Clone, Debug, PartialEq, Eq)]
-#[repr(u16)]
-#[derive(HeapSizeOf)]
-pub enum EventPhase {
-    None      = EventConstants::NONE,
-    Capturing = EventConstants::CAPTURING_PHASE,
-    AtTarget  = EventConstants::AT_TARGET,
-    Bubbling  = EventConstants::BUBBLING_PHASE,
-}
-
-#[derive(PartialEq, HeapSizeOf, Copy, Clone)]
-pub enum EventBubbles {
-    Bubbles,
-    DoesNotBubble
-}
-
-impl From<EventBubbles> for bool {
-    fn from(bubbles: EventBubbles) -> Self {
-        match bubbles {
-            EventBubbles::Bubbles => true,
-            EventBubbles::DoesNotBubble => false
-        }
-    }
-}
-
-impl From<bool> for EventBubbles {
-    fn from(boolean: bool) -> Self {
-        match boolean {
-            true => EventBubbles::Bubbles,
-            false => EventBubbles::DoesNotBubble
-        }
-    }
-}
-
-#[derive(PartialEq, HeapSizeOf, Copy, Clone)]
-pub enum EventCancelable {
-    Cancelable,
-    NotCancelable
-}
-
-impl From<EventCancelable> for bool {
-    fn from(bubbles: EventCancelable) -> Self {
-        match bubbles {
-            EventCancelable::Cancelable => true,
-            EventCancelable::NotCancelable => false
-        }
-    }
-}
-
-impl From<bool> for EventCancelable {
-    fn from(boolean: bool) -> Self {
-        match boolean {
-            true => EventCancelable::Cancelable,
-            false => EventCancelable::NotCancelable
-        }
-    }
-}
-
-/// An enum to indicate whether the default action of an event is allowed.
-///
-/// This should've been a bool. Instead, it's an enum, because, aside from the allowed/canceled
-/// states, we also need something to stop the event from being handled again (without cancelling
-/// the event entirely). For example, an Up/Down `KeyEvent` inside a `textarea` element will
-/// trigger the cursor to go up/down if the text inside the element spans multiple lines. This enum
-/// helps us to prevent such events from being [sent to the constellation][msg] where it will be
-/// handled once again for page scrolling (which is definitely not what we'd want).
-///
-/// [msg]: https://doc.servo.org/script_traits/enum.ConstellationMsg.html#variant.KeyEvent
-///
-#[derive(JSTraceable, HeapSizeOf, Copy, Clone, PartialEq)]
-pub enum EventDefault {
-    /// The default action of the event is allowed (constructor's default)
-    Allowed,
-    /// The default action has been prevented by calling `PreventDefault`
-    Prevented,
-    /// The event has been handled somewhere in the DOM, and it should be prevented from being
-    /// re-handled elsewhere. This doesn't affect the judgement of `DefaultPrevented`
-    Handled,
-}
-
 #[dom_struct]
 pub struct Event {
     reflector_: Reflector,
     current_target: MutNullableJS<EventTarget>,
     target: MutNullableJS<EventTarget>,
     type_: DOMRefCell<Atom>,
     phase: Cell<EventPhase>,
     canceled: Cell<EventDefault>,
@@ -171,79 +97,100 @@ impl Event {
         self.canceled.set(EventDefault::Allowed);
         self.trusted.set(false);
         self.target.set(None);
         *self.type_.borrow_mut() = type_;
         self.bubbles.set(bubbles);
         self.cancelable.set(cancelable);
     }
 
+    // https://dom.spec.whatwg.org/#concept-event-dispatch
+    pub fn dispatch(&self,
+                    target: &EventTarget,
+                    target_override: Option<&EventTarget>)
+                    -> EventStatus {
+        assert!(!self.dispatching());
+        assert!(self.initialized());
+        assert_eq!(self.phase.get(), EventPhase::None);
+        assert!(self.GetCurrentTarget().is_none());
+
+        // Step 1.
+        self.dispatching.set(true);
+
+        // Step 2.
+        self.target.set(Some(target_override.unwrap_or(target)));
+
+        if self.stop_propagation.get() {
+            // If the event's stop propagation flag is set, we can skip everything because
+            // it prevents the calls of the invoke algorithm in the spec.
+
+            // Step 10-12.
+            self.clear_dispatching_flags();
+
+            // Step 14.
+            return self.status();
+        }
+
+        // Step 3. The "invoke" algorithm is only used on `target` separately,
+        // so we don't put it in the path.
+        rooted_vec!(let mut event_path);
+
+        // Step 4.
+        if let Some(target_node) = target.downcast::<Node>() {
+            for ancestor in target_node.ancestors() {
+                event_path.push(JS::from_ref(ancestor.upcast::<EventTarget>()));
+            }
+            let top_most_ancestor_or_target =
+                Root::from_ref(event_path.r().last().cloned().unwrap_or(target));
+            if let Some(document) = Root::downcast::<Document>(top_most_ancestor_or_target) {
+                if self.type_() != atom!("load") && document.browsing_context().is_some() {
+                    event_path.push(JS::from_ref(document.window().upcast()));
+                }
+            }
+        }
+
+        // Steps 5-9. In a separate function to short-circuit various things easily.
+        dispatch_to_listeners(self, target, event_path.r());
+
+        // Default action.
+        if let Some(target) = self.GetTarget() {
+            if let Some(node) = target.downcast::<Node>() {
+                let vtable = vtable_for(&node);
+                vtable.handle_event(self);
+            }
+        }
+
+        // Step 10-12.
+        self.clear_dispatching_flags();
+
+        // Step 14.
+        self.status()
+    }
+
     pub fn status(&self) -> EventStatus {
         match self.DefaultPrevented() {
             true => EventStatus::Canceled,
             false => EventStatus::NotCanceled
         }
     }
 
     #[inline]
-    pub fn set_current_target(&self, val: &EventTarget) {
-        self.current_target.set(Some(val));
-    }
-
-    #[inline]
-    pub fn set_target(&self, val: &EventTarget) {
-        self.target.set(Some(val));
-    }
-
-    #[inline]
-    pub fn phase(&self) -> EventPhase {
-        self.phase.get()
-    }
-
-    #[inline]
-    pub fn set_phase(&self, val: EventPhase) {
-        self.phase.set(val)
-    }
-
-    #[inline]
-    pub fn stop_propagation(&self) -> bool {
-        self.stop_propagation.get()
-    }
-
-    #[inline]
-    pub fn stop_immediate(&self) -> bool {
-        self.stop_immediate.get()
-    }
-
-    #[inline]
-    pub fn bubbles(&self) -> bool {
-        self.bubbles.get()
-    }
-
-    #[inline]
     pub fn dispatching(&self) -> bool {
         self.dispatching.get()
     }
 
     #[inline]
-    // https://dom.spec.whatwg.org/#concept-event-dispatch Step 1.
-    pub fn mark_as_dispatching(&self) {
-        assert!(!self.dispatching.get());
-        self.dispatching.set(true);
-    }
-
-    #[inline]
     // https://dom.spec.whatwg.org/#concept-event-dispatch Steps 10-12.
-    pub fn clear_dispatching_flags(&self) {
+    fn clear_dispatching_flags(&self) {
         assert!(self.dispatching.get());
 
         self.dispatching.set(false);
         self.stop_propagation.set(false);
         self.stop_immediate.set(false);
-        self.set_phase(EventPhase::None);
+        self.phase.set(EventPhase::None);
         self.current_target.set(None);
     }
 
     #[inline]
     pub fn initialized(&self) -> bool {
         self.initialized.get()
     }
 
@@ -256,16 +203,26 @@ impl Event {
     pub fn mark_as_handled(&self) {
         self.canceled.set(EventDefault::Handled);
     }
 
     #[inline]
     pub fn get_cancel_state(&self) -> EventDefault {
         self.canceled.get()
     }
+
+    pub fn set_trusted(&self, trusted: bool) {
+        self.trusted.set(trusted);
+    }
+
+    // https://html.spec.whatwg.org/multipage/#fire-a-simple-event
+    pub fn fire(&self, target: &EventTarget) -> EventStatus {
+        self.set_trusted(true);
+        target.dispatch_event(self)
+    }
 }
 
 impl EventMethods for Event {
     // https://dom.spec.whatwg.org/#dom-event-eventphase
     fn EventPhase(&self) -> u16 {
         self.phase.get() as u16
     }
 
@@ -331,29 +288,102 @@ impl EventMethods for Event {
     }
 
     // https://dom.spec.whatwg.org/#dom-event-istrusted
     fn IsTrusted(&self) -> bool {
         self.trusted.get()
     }
 }
 
+#[derive(PartialEq, HeapSizeOf, Copy, Clone)]
+pub enum EventBubbles {
+    Bubbles,
+    DoesNotBubble
+}
 
-impl Event {
-    pub fn set_trusted(&self, trusted: bool) {
-        self.trusted.set(trusted);
+impl From<bool> for EventBubbles {
+    fn from(boolean: bool) -> Self {
+        match boolean {
+            true => EventBubbles::Bubbles,
+            false => EventBubbles::DoesNotBubble
+        }
+    }
+}
+
+impl From<EventBubbles> for bool {
+    fn from(bubbles: EventBubbles) -> Self {
+        match bubbles {
+            EventBubbles::Bubbles => true,
+            EventBubbles::DoesNotBubble => false
+        }
     }
+}
+
+#[derive(PartialEq, HeapSizeOf, Copy, Clone)]
+pub enum EventCancelable {
+    Cancelable,
+    NotCancelable
+}
+
+impl From<bool> for EventCancelable {
+    fn from(boolean: bool) -> Self {
+        match boolean {
+            true => EventCancelable::Cancelable,
+            false => EventCancelable::NotCancelable
+        }
+    }
+}
 
-    // https://html.spec.whatwg.org/multipage/#fire-a-simple-event
-    pub fn fire(&self, target: &EventTarget) -> EventStatus {
-        self.set_trusted(true);
-        target.dispatch_event(self)
+impl From<EventCancelable> for bool {
+    fn from(bubbles: EventCancelable) -> Self {
+        match bubbles {
+            EventCancelable::Cancelable => true,
+            EventCancelable::NotCancelable => false
+        }
     }
 }
 
+#[derive(JSTraceable, Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u16)]
+#[derive(HeapSizeOf)]
+pub enum EventPhase {
+    None      = EventConstants::NONE,
+    Capturing = EventConstants::CAPTURING_PHASE,
+    AtTarget  = EventConstants::AT_TARGET,
+    Bubbling  = EventConstants::BUBBLING_PHASE,
+}
+
+/// An enum to indicate whether the default action of an event is allowed.
+///
+/// This should've been a bool. Instead, it's an enum, because, aside from the allowed/canceled
+/// states, we also need something to stop the event from being handled again (without cancelling
+/// the event entirely). For example, an Up/Down `KeyEvent` inside a `textarea` element will
+/// trigger the cursor to go up/down if the text inside the element spans multiple lines. This enum
+/// helps us to prevent such events from being [sent to the constellation][msg] where it will be
+/// handled once again for page scrolling (which is definitely not what we'd want).
+///
+/// [msg]: https://doc.servo.org/script_traits/enum.ConstellationMsg.html#variant.KeyEvent
+///
+#[derive(JSTraceable, HeapSizeOf, Copy, Clone, PartialEq)]
+pub enum EventDefault {
+    /// The default action of the event is allowed (constructor's default)
+    Allowed,
+    /// The default action has been prevented by calling `PreventDefault`
+    Prevented,
+    /// The event has been handled somewhere in the DOM, and it should be prevented from being
+    /// re-handled elsewhere. This doesn't affect the judgement of `DefaultPrevented`
+    Handled,
+}
+
+#[derive(PartialEq)]
+pub enum EventStatus {
+    Canceled,
+    NotCanceled
+}
+
 // https://dom.spec.whatwg.org/#concept-event-fire
 pub struct EventRunnable {
     pub target: Trusted<EventTarget>,
     pub name: Atom,
     pub bubbles: EventBubbles,
     pub cancelable: EventCancelable,
 }
 
@@ -377,8 +407,121 @@ pub struct SimpleEventRunnable {
 impl Runnable for SimpleEventRunnable {
     fn name(&self) -> &'static str { "SimpleEventRunnable" }
 
     fn handler(self: Box<SimpleEventRunnable>) {
         let target = self.target.root();
         target.fire_event(self.name);
     }
 }
+
+// See dispatch_event.
+// https://dom.spec.whatwg.org/#concept-event-dispatch
+fn dispatch_to_listeners(event: &Event, target: &EventTarget, event_path: &[&EventTarget]) {
+    assert!(!event.stop_propagation.get());
+    assert!(!event.stop_immediate.get());
+
+    let window = match Root::downcast::<Window>(target.global()) {
+        Some(window) => {
+            if window.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
+                Some(window)
+            } else {
+                None
+            }
+        },
+        _ => None,
+    };
+
+    // Step 5.
+    event.phase.set(EventPhase::Capturing);
+
+    // Step 6.
+    for object in event_path.iter().rev() {
+        invoke(window.r(), object, event, Some(ListenerPhase::Capturing));
+        if event.stop_propagation.get() {
+            return;
+        }
+    }
+    assert!(!event.stop_propagation.get());
+    assert!(!event.stop_immediate.get());
+
+    // Step 7.
+    event.phase.set(EventPhase::AtTarget);
+
+    // Step 8.
+    invoke(window.r(), target, event, None);
+    if event.stop_propagation.get() {
+        return;
+    }
+    assert!(!event.stop_propagation.get());
+    assert!(!event.stop_immediate.get());
+
+    if !event.bubbles.get() {
+        return;
+    }
+
+    // Step 9.1.
+    event.phase.set(EventPhase::Bubbling);
+
+    // Step 9.2.
+    for object in event_path {
+        invoke(window.r(), object, event, Some(ListenerPhase::Bubbling));
+        if event.stop_propagation.get() {
+            return;
+        }
+    }
+}
+
+// https://dom.spec.whatwg.org/#concept-event-listener-invoke
+fn invoke(window: Option<&Window>,
+          object: &EventTarget,
+          event: &Event,
+          specific_listener_phase: Option<ListenerPhase>) {
+    // Step 1.
+    assert!(!event.stop_propagation.get());
+
+    // Steps 2-3.
+    let listeners = object.get_listeners_for(&event.type_(), specific_listener_phase);
+
+    // Step 4.
+    event.current_target.set(Some(object));
+
+    // Step 5.
+    inner_invoke(window, object, event, &listeners);
+
+    // TODO: step 6.
+}
+
+// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
+fn inner_invoke(window: Option<&Window>,
+                object: &EventTarget,
+                event: &Event,
+                listeners: &[CompiledEventListener])
+                -> bool {
+    // Step 1.
+    let mut found = false;
+
+    // Step 2.
+    for listener in listeners {
+        // Steps 2.1 and 2.3-2.4 are not done because `listeners` contain only the
+        // relevant ones for this invoke call during the dispatch algorithm.
+
+        // Step 2.2.
+        found = true;
+
+        // TODO: step 2.5.
+
+        // Step 2.6.
+        let marker = TimelineMarker::start("DOMEvent".to_owned());
+        listener.call_or_handle_event(object, event, ExceptionHandling::Report);
+        if let Some(window) = window {
+            window.emit_timeline_marker(marker.end());
+        }
+        if event.stop_immediate.get() {
+            return found;
+        }
+
+        // TODO: step 2.7.
+    }
+
+    // Step 3.
+    found
+}
deleted file mode 100644
--- a/servo/components/script/dom/eventdispatcher.rs
+++ /dev/null
@@ -1,223 +0,0 @@
-/* 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/. */
-
-use devtools_traits::{StartedTimelineMarker, TimelineMarker, TimelineMarkerType};
-use dom::bindings::callback::ExceptionHandling::Report;
-use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
-use dom::bindings::inheritance::Castable;
-use dom::bindings::js::{JS, Root, RootedReference};
-use dom::bindings::reflector::DomObject;
-use dom::document::Document;
-use dom::event::{Event, EventPhase};
-use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
-use dom::node::Node;
-use dom::virtualmethods::vtable_for;
-use dom::window::Window;
-
-struct AutoDOMEventMarker {
-    window: Root<Window>,
-    marker: Option<StartedTimelineMarker>,
-}
-
-impl AutoDOMEventMarker {
-    fn new(window: &Window) -> AutoDOMEventMarker {
-        AutoDOMEventMarker {
-            window: Root::from_ref(window),
-            marker: Some(TimelineMarker::start("DOMEvent".to_owned())),
-        }
-    }
-}
-
-impl Drop for AutoDOMEventMarker {
-    fn drop(&mut self) {
-        self.window.emit_timeline_marker(self.marker.take().unwrap().end());
-    }
-}
-
-fn handle_event(window: Option<&Window>, listener: &CompiledEventListener,
-                current_target: &EventTarget, event: &Event) {
-    let _marker;
-    if let Some(window) = window {
-        _marker = AutoDOMEventMarker::new(window);
-    }
-
-    listener.call_or_handle_event(current_target, event, Report);
-}
-
-// See dispatch_event.
-// https://dom.spec.whatwg.org/#concept-event-dispatch
-fn dispatch_to_listeners(event: &Event, target: &EventTarget, event_path: &[&EventTarget]) {
-    assert!(!event.stop_propagation());
-    assert!(!event.stop_immediate());
-
-    let window = match Root::downcast::<Window>(target.global()) {
-        Some(window) => {
-            if window.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
-                Some(window)
-            } else {
-                None
-            }
-        },
-        _ => None,
-    };
-
-    // Step 5.
-    event.set_phase(EventPhase::Capturing);
-
-    // Step 6.
-    for object in event_path.iter().rev() {
-        invoke(window.r(), object, event, Some(ListenerPhase::Capturing));
-        if event.stop_propagation() {
-            return;
-        }
-    }
-    assert!(!event.stop_propagation());
-    assert!(!event.stop_immediate());
-
-    // Step 7.
-    event.set_phase(EventPhase::AtTarget);
-
-    // Step 8.
-    invoke(window.r(), target, event, None);
-    if event.stop_propagation() {
-        return;
-    }
-    assert!(!event.stop_propagation());
-    assert!(!event.stop_immediate());
-
-    if !event.bubbles() {
-        return;
-    }
-
-    // Step 9.1.
-    event.set_phase(EventPhase::Bubbling);
-
-    // Step 9.2.
-    for object in event_path {
-        invoke(window.r(), object, event, Some(ListenerPhase::Bubbling));
-        if event.stop_propagation() {
-            return;
-        }
-    }
-}
-
-#[derive(PartialEq)]
-pub enum EventStatus {
-    Canceled,
-    NotCanceled
-}
-
-// https://dom.spec.whatwg.org/#concept-event-dispatch
-pub fn dispatch_event(target: &EventTarget,
-                      target_override: Option<&EventTarget>,
-                      event: &Event) -> EventStatus {
-    assert!(!event.dispatching());
-    assert!(event.initialized());
-    assert_eq!(event.phase(), EventPhase::None);
-    assert!(event.GetCurrentTarget().is_none());
-
-    // Step 1.
-    event.mark_as_dispatching();
-
-    // Step 2.
-    event.set_target(target_override.unwrap_or(target));
-
-    if event.stop_propagation() {
-        // If the event's stop propagation flag is set, we can skip everything because
-        // it prevents the calls of the invoke algorithm in the spec.
-
-        // Step 10-12.
-        event.clear_dispatching_flags();
-
-        // Step 14.
-        return event.status();
-    }
-
-    // Step 3. The "invoke" algorithm is only used on `target` separately,
-    // so we don't put it in the path.
-    rooted_vec!(let mut event_path);
-
-    // Step 4.
-    if let Some(target_node) = target.downcast::<Node>() {
-        for ancestor in target_node.ancestors() {
-            event_path.push(JS::from_ref(ancestor.upcast::<EventTarget>()));
-        }
-        let top_most_ancestor_or_target =
-            Root::from_ref(event_path.r().last().cloned().unwrap_or(target));
-        if let Some(document) = Root::downcast::<Document>(top_most_ancestor_or_target) {
-            if event.type_() != atom!("load") && document.browsing_context().is_some() {
-                event_path.push(JS::from_ref(document.window().upcast()));
-            }
-        }
-    }
-
-    // Steps 5-9. In a separate function to short-circuit various things easily.
-    dispatch_to_listeners(event, target, event_path.r());
-
-    // Default action.
-    if let Some(target) = event.GetTarget() {
-        if let Some(node) = target.downcast::<Node>() {
-            let vtable = vtable_for(&node);
-            vtable.handle_event(event);
-        }
-    }
-
-    // Step 10-12.
-    event.clear_dispatching_flags();
-
-    // Step 14.
-    event.status()
-}
-
-// https://dom.spec.whatwg.org/#concept-event-listener-invoke
-fn invoke(window: Option<&Window>,
-          object: &EventTarget,
-          event: &Event,
-          specific_listener_phase: Option<ListenerPhase>) {
-    // Step 1.
-    assert!(!event.stop_propagation());
-
-    // Steps 2-3.
-    let listeners = object.get_listeners_for(&event.type_(), specific_listener_phase);
-
-    // Step 4.
-    event.set_current_target(object);
-
-    // Step 5.
-    inner_invoke(window, object, event, &listeners);
-
-    // TODO: step 6.
-}
-
-// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
-fn inner_invoke(window: Option<&Window>,
-                object: &EventTarget,
-                event: &Event,
-                listeners: &[CompiledEventListener])
-                -> bool {
-    // Step 1.
-    let mut found = false;
-
-    // Step 2.
-    for listener in listeners {
-        // Steps 2.1 and 2.3-2.4 are not done because `listeners` contain only the
-        // relevant ones for this invoke call during the dispatch algorithm.
-
-        // Step 2.2.
-        found = true;
-
-        // TODO: step 2.5.
-
-        // Step 2.6.
-        handle_event(window, listener, object, event);
-        if event.stop_immediate() {
-            return found;
-        }
-
-        // TODO: step 2.7.
-    }
-
-    // Step 3.
-    found
-}
--- a/servo/components/script/dom/eventtarget.rs
+++ b/servo/components/script/dom/eventtarget.rs
@@ -17,18 +17,17 @@ use dom::bindings::codegen::Bindings::Wi
 use dom::bindings::codegen::UnionTypes::EventOrString;
 use dom::bindings::error::{Error, Fallible, report_pending_exception};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::Root;
 use dom::bindings::reflector::{DomObject, Reflector};
 use dom::bindings::str::DOMString;
 use dom::element::Element;
 use dom::errorevent::ErrorEvent;
-use dom::event::{Event, EventBubbles, EventCancelable};
-use dom::eventdispatcher::{EventStatus, dispatch_event};
+use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
 use dom::node::document_from_node;
 use dom::virtualmethods::VirtualMethods;
 use dom::window::Window;
 use fnv::FnvHasher;
 use heapsize::HeapSizeOf;
 use js::jsapi::{CompileFunction, JS_GetFunctionObject, JSAutoCompartment};
 use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
 use libc::{c_char, size_t};
@@ -295,21 +294,21 @@ impl EventTarget {
         self.handlers.borrow_mut().get_mut(type_).map_or(vec![], |listeners| {
             listeners.get_listeners(specific_phase, self, type_)
         })
     }
 
     pub fn dispatch_event_with_target(&self,
                                       target: &EventTarget,
                                       event: &Event) -> EventStatus {
-        dispatch_event(self, Some(target), event)
+        event.dispatch(self, Some(target))
     }
 
     pub fn dispatch_event(&self, event: &Event) -> EventStatus {
-        dispatch_event(self, None, event)
+        event.dispatch(self, None)
     }
 
     /// https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handlers-11
     fn set_inline_event_listener(&self,
                                  ty: Atom,
                                  listener: Option<InlineEventListener>) {
         let mut handlers = self.handlers.borrow_mut();
         let entries = match handlers.entry(ty) {
--- a/servo/components/script/dom/globalscope.rs
+++ b/servo/components/script/dom/globalscope.rs
@@ -10,18 +10,17 @@ use dom::bindings::error::{ErrorInfo, re
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{MutNullableJS, Root};
 use dom::bindings::reflector::DomObject;
 use dom::bindings::settings_stack::{AutoEntryScript, entry_global, incumbent_global};
 use dom::bindings::str::DOMString;
 use dom::crypto::Crypto;
 use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
 use dom::errorevent::ErrorEvent;
-use dom::event::{Event, EventBubbles, EventCancelable};
-use dom::eventdispatcher::EventStatus;
+use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
 use dom::eventtarget::EventTarget;
 use dom::window::Window;
 use dom::workerglobalscope::WorkerGlobalScope;
 use ipc_channel::ipc::IpcSender;
 use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
 use js::glue::{IsWrapper, UnwrapObject};
 use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment};
 use js::jsapi::{HandleValue, Evaluate2, JSAutoCompartment, JSContext};
--- a/servo/components/script/dom/htmlscriptelement.rs
+++ b/servo/components/script/dom/htmlscriptelement.rs
@@ -12,18 +12,17 @@ use dom::bindings::inheritance::Castable
 use dom::bindings::js::{JS, Root};
 use dom::bindings::js::RootedReference;
 use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::DomObject;
 use dom::bindings::str::DOMString;
 use dom::document::Document;
 use dom::element::{AttributeMutation, Element, ElementCreator};
 use dom::element::{cors_setting_for_element, reflect_cross_origin_attribute, set_cross_origin_attribute};
-use dom::event::{Event, EventBubbles, EventCancelable};
-use dom::eventdispatcher::EventStatus;
+use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
 use dom::globalscope::GlobalScope;
 use dom::htmlelement::HTMLElement;
 use dom::node::{ChildrenMutation, CloneChildrenFlag, Node};
 use dom::node::{document_from_node, window_from_node};
 use dom::virtualmethods::VirtualMethods;
 use encoding::label::encoding_from_whatwg_label;
 use encoding::types::{DecoderTrap, EncodingRef};
 use html5ever_atoms::LocalName;
--- a/servo/components/script/dom/mod.rs
+++ b/servo/components/script/dom/mod.rs
@@ -271,17 +271,16 @@ pub mod domquad;
 pub mod domrect;
 pub mod domrectlist;
 pub mod domrectreadonly;
 pub mod domstringmap;
 pub mod domtokenlist;
 pub mod element;
 pub mod errorevent;
 pub mod event;
-pub mod eventdispatcher;
 pub mod eventsource;
 pub mod eventtarget;
 pub mod extendableevent;
 pub mod extendablemessageevent;
 pub mod file;
 pub mod filelist;
 pub mod filereader;
 pub mod filereadersync;
--- a/servo/components/script/dom/worker.rs
+++ b/servo/components/script/dom/worker.rs
@@ -12,18 +12,17 @@ use dom::bindings::error::{Error, ErrorR
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::Root;
 use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::{DomObject, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::bindings::structuredclone::StructuredCloneData;
 use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
 use dom::errorevent::ErrorEvent;
-use dom::event::{Event, EventBubbles, EventCancelable};
-use dom::eventdispatcher::EventStatus;
+use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
 use dom::eventtarget::EventTarget;
 use dom::globalscope::GlobalScope;
 use dom::messageevent::MessageEvent;
 use dom::workerglobalscope::prepare_workerscope_init;
 use ipc_channel::ipc;
 use js::jsapi::{HandleValue, JSAutoCompartment, JSContext, NullHandleValue};
 use js::jsval::UndefinedValue;
 use script_thread::Runnable;