aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/eventtarget.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/eventtarget.rs')
-rw-r--r--components/script/dom/eventtarget.rs287
1 files changed, 287 insertions, 0 deletions
diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs
new file mode 100644
index 00000000000..4f2cba18def
--- /dev/null
+++ b/components/script/dom/eventtarget.rs
@@ -0,0 +1,287 @@
+/* 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 dom::bindings::callback::CallbackContainer;
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
+use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
+use dom::bindings::error::{Fallible, InvalidState, report_pending_exception};
+use dom::bindings::js::JSRef;
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::event::Event;
+use dom::eventdispatcher::dispatch_event;
+use dom::node::NodeTypeId;
+use dom::workerglobalscope::WorkerGlobalScopeId;
+use dom::xmlhttprequest::XMLHttpRequestId;
+use dom::virtualmethods::VirtualMethods;
+use js::jsapi::{JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject};
+use js::jsapi::{JSContext, JSObject};
+use servo_util::str::DOMString;
+use libc::{c_char, size_t};
+use std::cell::RefCell;
+use std::ptr;
+use url::Url;
+
+use std::collections::hashmap::HashMap;
+
+#[deriving(PartialEq,Encodable)]
+pub enum ListenerPhase {
+ Capturing,
+ Bubbling,
+}
+
+#[deriving(PartialEq,Encodable)]
+pub enum EventTargetTypeId {
+ NodeTargetTypeId(NodeTypeId),
+ WindowTypeId,
+ WorkerTypeId,
+ WorkerGlobalScopeTypeId(WorkerGlobalScopeId),
+ XMLHttpRequestTargetTypeId(XMLHttpRequestId)
+}
+
+#[deriving(PartialEq, Encodable)]
+pub enum EventListenerType {
+ Additive(EventListener),
+ Inline(EventListener),
+}
+
+impl EventListenerType {
+ fn get_listener(&self) -> EventListener {
+ match *self {
+ Additive(listener) | Inline(listener) => listener
+ }
+ }
+}
+
+#[deriving(PartialEq,Encodable)]
+pub struct EventListenerEntry {
+ pub phase: ListenerPhase,
+ pub listener: EventListenerType
+}
+
+#[deriving(Encodable)]
+pub struct EventTarget {
+ pub type_id: EventTargetTypeId,
+ reflector_: Reflector,
+ handlers: Traceable<RefCell<HashMap<DOMString, Vec<EventListenerEntry>>>>,
+}
+
+impl EventTarget {
+ pub fn new_inherited(type_id: EventTargetTypeId) -> EventTarget {
+ EventTarget {
+ type_id: type_id,
+ reflector_: Reflector::new(),
+ handlers: Traceable::new(RefCell::new(HashMap::new())),
+ }
+ }
+
+ pub fn get_listeners(&self, type_: &str) -> Option<Vec<EventListener>> {
+ self.handlers.deref().borrow().find_equiv(&type_).map(|listeners| {
+ listeners.iter().map(|entry| entry.listener.get_listener()).collect()
+ })
+ }
+
+ pub fn get_listeners_for(&self, type_: &str, desired_phase: ListenerPhase)
+ -> Option<Vec<EventListener>> {
+ self.handlers.deref().borrow().find_equiv(&type_).map(|listeners| {
+ let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
+ filtered.map(|entry| entry.listener.get_listener()).collect()
+ })
+ }
+}
+
+pub trait EventTargetHelpers {
+ fn dispatch_event_with_target<'a>(&self,
+ target: Option<JSRef<'a, EventTarget>>,
+ event: &JSRef<Event>) -> Fallible<bool>;
+ fn set_inline_event_listener(&self,
+ ty: DOMString,
+ listener: Option<EventListener>);
+ fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener>;
+ fn set_event_handler_uncompiled(&self,
+ cx: *mut JSContext,
+ url: Url,
+ scope: *mut JSObject,
+ ty: &str,
+ source: DOMString);
+ fn set_event_handler_common<T: CallbackContainer>(&self, ty: &str,
+ listener: Option<T>);
+ fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<T>;
+
+ fn has_handlers(&self) -> bool;
+}
+
+impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> {
+ fn dispatch_event_with_target<'b>(&self,
+ target: Option<JSRef<'b, EventTarget>>,
+ event: &JSRef<Event>) -> Fallible<bool> {
+ if event.deref().dispatching.deref().get() || !event.deref().initialized.deref().get() {
+ return Err(InvalidState);
+ }
+ Ok(dispatch_event(self, target, event))
+ }
+
+ fn set_inline_event_listener(&self,
+ ty: DOMString,
+ listener: Option<EventListener>) {
+ let mut handlers = self.handlers.deref().borrow_mut();
+ let entries = handlers.find_or_insert_with(ty, |_| vec!());
+ let idx = entries.iter().position(|&entry| {
+ match entry.listener {
+ Inline(_) => true,
+ _ => false,
+ }
+ });
+
+ match idx {
+ Some(idx) => {
+ match listener {
+ Some(listener) => entries.get_mut(idx).listener = Inline(listener),
+ None => {
+ entries.remove(idx);
+ }
+ }
+ }
+ None => {
+ if listener.is_some() {
+ entries.push(EventListenerEntry {
+ phase: Bubbling,
+ listener: Inline(listener.unwrap()),
+ });
+ }
+ }
+ }
+ }
+
+ fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener> {
+ let handlers = self.handlers.deref().borrow();
+ let entries = handlers.find(&ty);
+ entries.and_then(|entries| entries.iter().find(|entry| {
+ match entry.listener {
+ Inline(_) => true,
+ _ => false,
+ }
+ }).map(|entry| entry.listener.get_listener()))
+ }
+
+ fn set_event_handler_uncompiled(&self,
+ cx: *mut JSContext,
+ url: Url,
+ scope: *mut JSObject,
+ ty: &str,
+ source: DOMString) {
+ let url = url.serialize().to_c_str();
+ let name = ty.to_c_str();
+ let lineno = 0; //XXXjdm need to get a real number here
+
+ let nargs = 1; //XXXjdm not true for onerror
+ static arg_name: [c_char, ..6] =
+ ['e' as c_char, 'v' as c_char, 'e' as c_char, 'n' as c_char, 't' as c_char, 0];
+ static arg_names: [*const c_char, ..1] = [&arg_name as *const c_char];
+
+ let source: Vec<u16> = source.as_slice().utf16_units().collect();
+ let handler = unsafe {
+ JS_CompileUCFunction(cx,
+ ptr::mut_null(),
+ name.as_ptr(),
+ nargs,
+ &arg_names as *const *const i8 as *mut *const i8,
+ source.as_ptr(),
+ source.len() as size_t,
+ url.as_ptr(),
+ lineno)
+ };
+ if handler.is_null() {
+ report_pending_exception(cx, self.reflector().get_jsobject());
+ return;
+ }
+
+ let funobj = unsafe {
+ JS_CloneFunctionObject(cx, JS_GetFunctionObject(handler), scope)
+ };
+ assert!(funobj.is_not_null());
+ self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)));
+ }
+
+ fn set_event_handler_common<T: CallbackContainer>(
+ &self, ty: &str, listener: Option<T>)
+ {
+ let event_listener = listener.map(|listener|
+ EventListener::new(listener.callback()));
+ self.set_inline_event_listener(ty.to_string(), event_listener);
+ }
+
+ fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<T> {
+ let listener = self.get_inline_event_listener(ty.to_string());
+ listener.map(|listener| CallbackContainer::new(listener.parent.callback()))
+ }
+
+ fn has_handlers(&self) -> bool {
+ !self.handlers.deref().borrow().is_empty()
+ }
+}
+
+impl<'a> EventTargetMethods for JSRef<'a, EventTarget> {
+ fn AddEventListener(&self,
+ ty: DOMString,
+ listener: Option<EventListener>,
+ capture: bool) {
+ match listener {
+ Some(listener) => {
+ let mut handlers = self.handlers.deref().borrow_mut();
+ let entry = handlers.find_or_insert_with(ty, |_| vec!());
+ let phase = if capture { Capturing } else { Bubbling };
+ let new_entry = EventListenerEntry {
+ phase: phase,
+ listener: Additive(listener)
+ };
+ if entry.as_slice().position_elem(&new_entry).is_none() {
+ entry.push(new_entry);
+ }
+ },
+ _ => (),
+ }
+ }
+
+ fn RemoveEventListener(&self,
+ ty: DOMString,
+ listener: Option<EventListener>,
+ capture: bool) {
+ match listener {
+ Some(listener) => {
+ let mut handlers = self.handlers.deref().borrow_mut();
+ let mut entry = handlers.find_mut(&ty);
+ for entry in entry.mut_iter() {
+ let phase = if capture { Capturing } else { Bubbling };
+ let old_entry = EventListenerEntry {
+ phase: phase,
+ listener: Additive(listener)
+ };
+ let position = entry.as_slice().position_elem(&old_entry);
+ for &position in position.iter() {
+ entry.remove(position);
+ }
+ }
+ },
+ _ => (),
+ }
+ }
+
+ fn DispatchEvent(&self, event: &JSRef<Event>) -> Fallible<bool> {
+ self.dispatch_event_with_target(None, event)
+ }
+}
+
+impl Reflectable for EventTarget {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, EventTarget> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ None
+ }
+}