/* 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>>>, } 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> { 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> { 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>, event: &JSRef) -> Fallible; fn set_inline_event_listener(&self, ty: DOMString, listener: Option); fn get_inline_event_listener(&self, ty: DOMString) -> Option; fn set_event_handler_uncompiled(&self, cx: *mut JSContext, url: Url, scope: *mut JSObject, ty: &str, source: DOMString); fn set_event_handler_common(&self, ty: &str, listener: Option); fn get_event_handler_common(&self, ty: &str) -> Option; fn has_handlers(&self) -> bool; } impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> { fn dispatch_event_with_target<'b>(&self, target: Option>, event: &JSRef) -> Fallible { 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) { 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 { 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 = 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( &self, ty: &str, listener: Option) { 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(&self, ty: &str) -> Option { 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, 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, 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) -> Fallible { 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 } }