mirror of
https://github.com/servo/servo.git
synced 2025-06-14 19:34:29 +00:00
The util component specified fnv and smallvec as dependencies and publicly reexported both of them. Several other components utilized these reexports, presumably because fnv and smallvec used to live in the tree so reexporting made the transition easier. These indirect dependencies through the util component are unnecessary. This commit removes the fnv & smallvec crate reexports in the util component. It exchange, it adds fnv & smallvec as dependencies to non-util components wherever needed. Finally, it removes the fnv dependency from util as it is not utilized anywhere in the util component.
315 lines
11 KiB
Rust
315 lines
11 KiB
Rust
/* 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::cell::DOMRefCell;
|
|
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, report_pending_exception};
|
|
use dom::bindings::error::Error::InvalidState;
|
|
use dom::bindings::js::JSRef;
|
|
use dom::bindings::utils::{Reflectable, Reflector};
|
|
use dom::event::{Event, EventHelpers};
|
|
use dom::eventdispatcher::dispatch_event;
|
|
use dom::node::NodeTypeId;
|
|
use dom::workerglobalscope::WorkerGlobalScopeTypeId;
|
|
use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTargetTypeId;
|
|
use dom::virtualmethods::VirtualMethods;
|
|
use js::jsapi::{JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject};
|
|
use js::jsapi::{JSContext, JSObject};
|
|
use util::str::DOMString;
|
|
|
|
use fnv::FnvHasher;
|
|
use libc::{c_char, size_t};
|
|
use std::borrow::ToOwned;
|
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
|
use std::collections::hash_state::DefaultState;
|
|
use std::default::Default;
|
|
use std::ffi::CString;
|
|
use std::ptr;
|
|
use url::Url;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
#[jstraceable]
|
|
pub enum ListenerPhase {
|
|
Capturing,
|
|
Bubbling,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
#[jstraceable]
|
|
pub enum EventTargetTypeId {
|
|
Node(NodeTypeId),
|
|
WebSocket,
|
|
Window,
|
|
Worker,
|
|
WorkerGlobalScope(WorkerGlobalScopeTypeId),
|
|
XMLHttpRequestEventTarget(XMLHttpRequestEventTargetTypeId)
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
#[jstraceable]
|
|
pub enum EventListenerType {
|
|
Additive(EventListener),
|
|
Inline(EventListener),
|
|
}
|
|
|
|
impl EventListenerType {
|
|
fn get_listener(&self) -> EventListener {
|
|
match *self {
|
|
EventListenerType::Additive(listener) |
|
|
EventListenerType::Inline(listener) => listener
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
#[jstraceable]
|
|
#[privatize]
|
|
pub struct EventListenerEntry {
|
|
phase: ListenerPhase,
|
|
listener: EventListenerType
|
|
}
|
|
|
|
#[dom_struct]
|
|
pub struct EventTarget {
|
|
reflector_: Reflector,
|
|
type_id: EventTargetTypeId,
|
|
handlers: DOMRefCell<HashMap<DOMString, Vec<EventListenerEntry>, DefaultState<FnvHasher>>>,
|
|
}
|
|
|
|
impl EventTarget {
|
|
pub fn new_inherited(type_id: EventTargetTypeId) -> EventTarget {
|
|
EventTarget {
|
|
reflector_: Reflector::new(),
|
|
type_id: type_id,
|
|
handlers: DOMRefCell::new(Default::default()),
|
|
}
|
|
}
|
|
|
|
pub fn get_listeners(&self, type_: &str) -> Option<Vec<EventListener>> {
|
|
self.handlers.borrow().get(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.borrow().get(type_).map(|listeners| {
|
|
let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
|
|
filtered.map(|entry| entry.listener.get_listener()).collect()
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
pub fn type_id<'a>(&'a self) -> &'a EventTargetTypeId {
|
|
&self.type_id
|
|
}
|
|
}
|
|
|
|
pub trait EventTargetHelpers {
|
|
fn dispatch_event_with_target(self,
|
|
target: JSRef<EventTarget>,
|
|
event: JSRef<Event>) -> bool;
|
|
fn dispatch_event(self, event: JSRef<Event>) -> 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(self,
|
|
target: JSRef<EventTarget>,
|
|
event: JSRef<Event>) -> bool {
|
|
dispatch_event(self, Some(target), event)
|
|
}
|
|
|
|
fn dispatch_event(self, event: JSRef<Event>) -> bool {
|
|
dispatch_event(self, None, event)
|
|
}
|
|
|
|
fn set_inline_event_listener(self,
|
|
ty: DOMString,
|
|
listener: Option<EventListener>) {
|
|
let mut handlers = self.handlers.borrow_mut();
|
|
let entries = match handlers.entry(ty) {
|
|
Occupied(entry) => entry.into_mut(),
|
|
Vacant(entry) => entry.insert(vec!()),
|
|
};
|
|
|
|
let idx = entries.iter().position(|&entry| {
|
|
match entry.listener {
|
|
EventListenerType::Inline(_) => true,
|
|
_ => false,
|
|
}
|
|
});
|
|
|
|
match idx {
|
|
Some(idx) => {
|
|
match listener {
|
|
Some(listener) => entries[idx].listener = EventListenerType::Inline(listener),
|
|
None => {
|
|
entries.remove(idx);
|
|
}
|
|
}
|
|
}
|
|
None => {
|
|
if listener.is_some() {
|
|
entries.push(EventListenerEntry {
|
|
phase: ListenerPhase::Bubbling,
|
|
listener: EventListenerType::Inline(listener.unwrap()),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_inline_event_listener(self, ty: DOMString) -> Option<EventListener> {
|
|
let handlers = self.handlers.borrow();
|
|
let entries = handlers.get(&ty);
|
|
entries.and_then(|entries| entries.iter().find(|entry| {
|
|
match entry.listener {
|
|
EventListenerType::Inline(_) => true,
|
|
_ => false,
|
|
}
|
|
}).map(|entry| entry.listener.get_listener()))
|
|
}
|
|
|
|
#[allow(unsafe_code)]
|
|
fn set_event_handler_uncompiled(self,
|
|
cx: *mut JSContext,
|
|
url: Url,
|
|
scope: *mut JSObject,
|
|
ty: &str,
|
|
source: DOMString) {
|
|
let url = CString::new(url.serialize()).unwrap();
|
|
let name = CString::new(ty).unwrap();
|
|
let lineno = 0; //XXXjdm need to get a real number here
|
|
|
|
let nargs = 1; //XXXjdm not true for onerror
|
|
static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char];
|
|
|
|
let source: Vec<u16> = source.utf16_units().collect();
|
|
let handler = unsafe {
|
|
JS_CompileUCFunction(cx,
|
|
ptr::null_mut(),
|
|
name.as_ptr(),
|
|
nargs,
|
|
ARG_NAMES.as_mut_ptr(),
|
|
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_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_owned(), event_listener);
|
|
}
|
|
|
|
fn get_event_handler_common<T: CallbackContainer>(self, ty: &str) -> Option<T> {
|
|
let listener = self.get_inline_event_listener(ty.to_owned());
|
|
listener.map(|listener| CallbackContainer::new(listener.parent.callback()))
|
|
}
|
|
|
|
fn has_handlers(self) -> bool {
|
|
// FIXME(https://github.com/rust-lang/rust/issues/23338)
|
|
let handlers = self.handlers.borrow();
|
|
!handlers.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.borrow_mut();
|
|
let entry = match handlers.entry(ty) {
|
|
Occupied(entry) => entry.into_mut(),
|
|
Vacant(entry) => entry.insert(vec!()),
|
|
};
|
|
|
|
let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
|
|
let new_entry = EventListenerEntry {
|
|
phase: phase,
|
|
listener: EventListenerType::Additive(listener)
|
|
};
|
|
if entry.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.borrow_mut();
|
|
let mut entry = handlers.get_mut(&ty);
|
|
for entry in entry.iter_mut() {
|
|
let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
|
|
let old_entry = EventListenerEntry {
|
|
phase: phase,
|
|
listener: EventListenerType::Additive(listener)
|
|
};
|
|
let position = entry.position_elem(&old_entry);
|
|
for &position in position.iter() {
|
|
entry.remove(position);
|
|
}
|
|
}
|
|
},
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
fn DispatchEvent(self, event: JSRef<Event>) -> Fallible<bool> {
|
|
if event.dispatching() || !event.initialized() {
|
|
return Err(InvalidState);
|
|
}
|
|
event.set_trusted(false);
|
|
Ok(self.dispatch_event(event))
|
|
}
|
|
}
|
|
|
|
impl<'a> VirtualMethods for JSRef<'a, EventTarget> {
|
|
fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
|
|
None
|
|
}
|
|
}
|