diff options
Diffstat (limited to 'components/script/dom/eventtarget.rs')
-rw-r--r-- | components/script/dom/eventtarget.rs | 738 |
1 files changed, 469 insertions, 269 deletions
diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index f45942f0f05..eb27f63069f 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -1,54 +1,64 @@ /* 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::beforeunloadevent::BeforeUnloadEvent; -use dom::bindings::callback::{CallbackContainer, ExceptionHandling, CallbackFunction}; -use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEventMethods; -use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods; -use dom::bindings::codegen::Bindings::EventBinding::EventMethods; -use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; -use dom::bindings::codegen::Bindings::EventHandlerBinding::OnBeforeUnloadEventHandlerNonNull; -use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull; -use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener; -use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -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, EventStatus}; -use dom::node::document_from_node; -use dom::virtualmethods::VirtualMethods; -use dom::window::Window; + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::beforeunloadevent::BeforeUnloadEvent; +use crate::dom::bindings::callback::{CallbackContainer, CallbackFunction, ExceptionHandling}; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEventMethods; +use crate::dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods; +use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods; +use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; +use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::OnBeforeUnloadEventHandlerNonNull; +use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull; +use crate::dom::bindings::codegen::Bindings::EventListenerBinding::EventListener; +use crate::dom::bindings::codegen::Bindings::EventTargetBinding::AddEventListenerOptions; +use crate::dom::bindings::codegen::Bindings::EventTargetBinding::EventListenerOptions; +use crate::dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; +use crate::dom::bindings::codegen::UnionTypes::AddEventListenerOptionsOrBoolean; +use crate::dom::bindings::codegen::UnionTypes::EventListenerOptionsOrBoolean; +use crate::dom::bindings::codegen::UnionTypes::EventOrString; +use crate::dom::bindings::error::{report_pending_exception, Error, Fallible}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::element::Element; +use crate::dom::errorevent::ErrorEvent; +use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlformelement::FormControlElementHelpers; +use crate::dom::node::document_from_node; +use crate::dom::virtualmethods::VirtualMethods; +use crate::dom::window::Window; +use crate::dom::workerglobalscope::WorkerGlobalScope; +use crate::realms::{enter_realm, InRealm}; use dom_struct::dom_struct; use fnv::FnvHasher; -use heapsize::HeapSizeOf; -use js::jsapi::{CompileFunction, JS_GetFunctionObject, JSAutoCompartment}; -use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper}; -use libc::{c_char, size_t}; +use js::jsapi::JS_GetFunctionObject; +use js::rust::transform_u16_to_source_text; +use js::rust::wrappers::CompileFunction; +use js::rust::{CompileOptionsWrapper, RootedObjectVectorWrapper}; +use libc::c_char; use servo_atoms::Atom; use servo_url::ServoUrl; -use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::collections::HashMap; use std::default::Default; use std::ffi::CString; use std::hash::BuildHasherDefault; use std::mem; use std::ops::{Deref, DerefMut}; -use std::ptr; use std::rc::Rc; -#[derive(PartialEq, Clone, JSTraceable)] +#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)] pub enum CommonEventHandler { - EventHandler(Rc<EventHandlerNonNull>), - ErrorEventHandler(Rc<OnErrorEventHandlerNonNull>), - BeforeUnloadEventHandler(Rc<OnBeforeUnloadEventHandlerNonNull>), + EventHandler(#[ignore_malloc_size_of = "Rc"] Rc<EventHandlerNonNull>), + + ErrorEventHandler(#[ignore_malloc_size_of = "Rc"] Rc<OnErrorEventHandlerNonNull>), + + BeforeUnloadEventHandler(#[ignore_malloc_size_of = "Rc"] Rc<OnBeforeUnloadEventHandlerNonNull>), } impl CommonEventHandler { @@ -61,14 +71,14 @@ impl CommonEventHandler { } } -#[derive(JSTraceable, Copy, Clone, PartialEq, HeapSizeOf)] +#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)] pub enum ListenerPhase { Capturing, Bubbling, } -/// https://html.spec.whatwg.org/multipage/#internal-raw-uncompiled-handler -#[derive(JSTraceable, Clone, PartialEq)] +/// <https://html.spec.whatwg.org/multipage/#internal-raw-uncompiled-handler> +#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)] struct InternalRawUncompiledHandler { source: DOMString, url: ServoUrl, @@ -76,7 +86,7 @@ struct InternalRawUncompiledHandler { } /// A representation of an event handler, either compiled or uncompiled raw source, or null. -#[derive(JSTraceable, PartialEq, Clone)] +#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)] enum InlineEventListener { Uncompiled(InternalRawUncompiledHandler), Compiled(CommonEventHandler), @@ -86,9 +96,12 @@ enum InlineEventListener { impl InlineEventListener { /// Get a compiled representation of this event handler, compiling it from its /// raw source if necessary. - /// https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler - fn get_compiled_handler(&mut self, owner: &EventTarget, ty: &Atom) - -> Option<CommonEventHandler> { + /// <https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler> + fn get_compiled_handler( + &mut self, + owner: &EventTarget, + ty: &Atom, + ) -> Option<CommonEventHandler> { match mem::replace(self, InlineEventListener::Null) { InlineEventListener::Null => None, InlineEventListener::Uncompiled(handler) => { @@ -97,37 +110,34 @@ impl InlineEventListener { *self = InlineEventListener::Compiled(compiled.clone()); } result - } + }, InlineEventListener::Compiled(handler) => { *self = InlineEventListener::Compiled(handler.clone()); Some(handler) - } + }, } } } -#[derive(JSTraceable, Clone, PartialEq)] +#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)] enum EventListenerType { - Additive(Rc<EventListener>), + Additive(#[ignore_malloc_size_of = "Rc"] Rc<EventListener>), Inline(InlineEventListener), } -impl HeapSizeOf for EventListenerType { - fn heap_size_of_children(&self) -> usize { - // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871 - 0 - } -} - impl EventListenerType { - fn get_compiled_listener(&mut self, owner: &EventTarget, ty: &Atom) - -> Option<CompiledEventListener> { + fn get_compiled_listener( + &mut self, + owner: &EventTarget, + ty: &Atom, + ) -> Option<CompiledEventListener> { match self { - &mut EventListenerType::Inline(ref mut inline) => - inline.get_compiled_handler(owner, ty) - .map(CompiledEventListener::Handler), - &mut EventListenerType::Additive(ref listener) => - Some(CompiledEventListener::Listener(listener.clone())), + &mut EventListenerType::Inline(ref mut inline) => inline + .get_compiled_handler(owner, ty) + .map(CompiledEventListener::Handler), + &mut EventListenerType::Additive(ref listener) => { + Some(CompiledEventListener::Listener(listener.clone())) + }, } } } @@ -141,11 +151,29 @@ pub enum CompiledEventListener { impl CompiledEventListener { #[allow(unsafe_code)] + pub fn associated_global(&self) -> DomRoot<GlobalScope> { + let obj = match self { + CompiledEventListener::Listener(listener) => listener.callback(), + CompiledEventListener::Handler(CommonEventHandler::EventHandler(handler)) => { + handler.callback() + }, + CompiledEventListener::Handler(CommonEventHandler::ErrorEventHandler(handler)) => { + handler.callback() + }, + CompiledEventListener::Handler(CommonEventHandler::BeforeUnloadEventHandler( + handler, + )) => handler.callback(), + }; + unsafe { GlobalScope::from_object(obj) } + } + // https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm - pub fn call_or_handle_event<T: DomObject>(&self, - object: &T, - event: &Event, - exception_handle: ExceptionHandling) { + pub fn call_or_handle_event( + &self, + object: &EventTarget, + event: &Event, + exception_handle: ExceptionHandling, + ) { // Step 3 match *self { CompiledEventListener::Listener(ref listener) => { @@ -155,80 +183,103 @@ impl CompiledEventListener { match *handler { CommonEventHandler::ErrorEventHandler(ref handler) => { if let Some(event) = event.downcast::<ErrorEvent>() { - let cx = object.global().get_cx(); - rooted!(in(cx) let error = unsafe { event.Error(cx) }); - let return_value = handler.Call_(object, - EventOrString::String(event.Message()), - Some(event.Filename()), - Some(event.Lineno()), - Some(event.Colno()), - Some(error.handle()), - exception_handle); - // Step 4 - if let Ok(return_value) = return_value { - rooted!(in(cx) let return_value = return_value); - if return_value.handle().is_boolean() && return_value.handle().to_boolean() == true { - event.upcast::<Event>().PreventDefault(); + if object.is::<Window>() || object.is::<WorkerGlobalScope>() { + let cx = object.global().get_cx(); + rooted!(in(*cx) let error = event.Error(cx)); + let return_value = handler.Call_( + object, + EventOrString::String(event.Message()), + Some(event.Filename()), + Some(event.Lineno()), + Some(event.Colno()), + Some(error.handle()), + exception_handle, + ); + // Step 4 + if let Ok(return_value) = return_value { + rooted!(in(*cx) let return_value = return_value); + if return_value.handle().is_boolean() && + return_value.handle().to_boolean() == true + { + event.upcast::<Event>().PreventDefault(); + } } + return; } - return; } - let _ = handler.Call_(object, EventOrString::Event(Root::from_ref(event)), - None, None, None, None, exception_handle); - } + let _ = handler.Call_( + object, + EventOrString::Event(DomRoot::from_ref(event)), + None, + None, + None, + None, + exception_handle, + ); + }, CommonEventHandler::BeforeUnloadEventHandler(ref handler) => { if let Some(event) = event.downcast::<BeforeUnloadEvent>() { - let rv = event.ReturnValue(); - - if let Ok(value) = handler.Call_(object, - event.upcast::<Event>(), - exception_handle) { - match value { - Some(value) => { - if rv.is_empty() { - event.SetReturnValue(value); - } - } - None => { - event.upcast::<Event>().PreventDefault(); + // Step 5 + if let Ok(value) = + handler.Call_(object, event.upcast::<Event>(), exception_handle) + { + let rv = event.ReturnValue(); + if let Some(v) = value { + if rv.is_empty() { + event.SetReturnValue(v); } + event.upcast::<Event>().PreventDefault(); } } + } else { + // Step 5, "Otherwise" clause + let _ = + handler.Call_(object, event.upcast::<Event>(), exception_handle); } - } + }, CommonEventHandler::EventHandler(ref handler) => { if let Ok(value) = handler.Call_(object, event, exception_handle) { let cx = object.global().get_cx(); - rooted!(in(cx) let value = value); + rooted!(in(*cx) let value = value); let value = value.handle(); - //Step 4 - let should_cancel = match event.type_() { - atom!("mouseover") => value.is_boolean() && value.to_boolean() == true, - _ => value.is_boolean() && value.to_boolean() == false - }; + //Step 5 + let should_cancel = value.is_boolean() && value.to_boolean() == false; + if should_cancel { + // FIXME: spec says to set the cancelled flag directly + // here, not just to prevent default; + // can that ever make a difference? event.PreventDefault(); } } - } + }, } - } + }, } } } -#[derive(Clone, DenyPublicFields, HeapSizeOf, JSTraceable, PartialEq)] +// https://dom.spec.whatwg.org/#concept-event-listener +// (as distinct from https://dom.spec.whatwg.org/#callbackdef-eventlistener) +#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)] /// A listener in a collection of event listeners. struct EventListenerEntry { phase: ListenerPhase, - listener: EventListenerType + listener: EventListenerType, + once: bool, +} + +impl std::cmp::PartialEq for EventListenerEntry { + fn eq(&self, other: &Self) -> bool { + self.phase == other.phase && self.listener == other.listener + } } -#[derive(JSTraceable, HeapSizeOf)] +#[derive(JSTraceable, MallocSizeOf)] /// A mix of potentially uncompiled and compiled event listeners of the same type. struct EventListeners(Vec<EventListenerEntry>); @@ -247,7 +298,11 @@ impl DerefMut for EventListeners { impl EventListeners { // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler - fn get_inline_listener(&mut self, owner: &EventTarget, ty: &Atom) -> Option<CommonEventHandler> { + fn get_inline_listener( + &mut self, + owner: &EventTarget, + ty: &Atom, + ) -> Option<CommonEventHandler> { for entry in &mut self.0 { if let EventListenerType::Inline(ref mut inline) = entry.listener { // Step 1.1-1.8 and Step 2 @@ -260,234 +315,323 @@ impl EventListeners { } // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler - fn get_listeners(&mut self, phase: Option<ListenerPhase>, owner: &EventTarget, ty: &Atom) - -> Vec<CompiledEventListener> { - self.0.iter_mut().filter_map(|entry| { - if phase.is_none() || Some(entry.phase) == phase { - // Step 1.1-1.8, 2 - entry.listener.get_compiled_listener(owner, ty) - } else { - None - } - }).collect() + fn get_listeners( + &mut self, + phase: Option<ListenerPhase>, + owner: &EventTarget, + ty: &Atom, + ) -> Vec<CompiledEventListener> { + self.0 + .iter_mut() + .filter_map(|entry| { + if phase.is_none() || Some(entry.phase) == phase { + // Step 1.1-1.8, 2 + entry.listener.get_compiled_listener(owner, ty) + } else { + None + } + }) + .collect() + } + + fn has_listeners(&self) -> bool { + // TODO: add, and take into account, a 'removed' field? + // https://dom.spec.whatwg.org/#event-listener-removed + self.0.len() > 0 } } #[dom_struct] pub struct EventTarget { reflector_: Reflector, - handlers: DOMRefCell<HashMap<Atom, EventListeners, BuildHasherDefault<FnvHasher>>>, + handlers: DomRefCell<HashMap<Atom, EventListeners, BuildHasherDefault<FnvHasher>>>, } impl EventTarget { pub fn new_inherited() -> EventTarget { EventTarget { reflector_: Reflector::new(), - handlers: DOMRefCell::new(Default::default()), + handlers: DomRefCell::new(Default::default()), } } - pub fn get_listeners_for(&self, - type_: &Atom, - specific_phase: Option<ListenerPhase>) - -> Vec<CompiledEventListener> { - self.handlers.borrow_mut().get_mut(type_).map_or(vec![], |listeners| { - listeners.get_listeners(specific_phase, self, type_) - }) + fn new(global: &GlobalScope) -> DomRoot<EventTarget> { + reflect_dom_object(Box::new(EventTarget::new_inherited()), global) } - pub fn dispatch_event_with_target(&self, - target: &EventTarget, - event: &Event) -> EventStatus { - event.dispatch(self, Some(target)) + #[allow(non_snake_case)] + pub fn Constructor(global: &GlobalScope) -> Fallible<DomRoot<EventTarget>> { + Ok(EventTarget::new(global)) + } + + pub fn has_listeners_for(&self, type_: &Atom) -> bool { + match self.handlers.borrow().get(type_) { + Some(listeners) => listeners.has_listeners(), + None => false, + } + } + + pub fn get_listeners_for( + &self, + type_: &Atom, + specific_phase: Option<ListenerPhase>, + ) -> Vec<CompiledEventListener> { + self.handlers + .borrow_mut() + .get_mut(type_) + .map_or(vec![], |listeners| { + listeners.get_listeners(specific_phase, self, type_) + }) } pub fn dispatch_event(&self, event: &Event) -> EventStatus { - event.dispatch(self, None) + if let Some(window) = self.global().downcast::<Window>() { + if window.has_document() { + assert!(window.Document().can_invoke_script()); + } + }; + event.dispatch(self, false) } pub fn remove_all_listeners(&self) { *self.handlers.borrow_mut() = Default::default(); } - /// https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handlers-11 - fn set_inline_event_listener(&self, - ty: Atom, - listener: Option<InlineEventListener>) { + /// <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) { Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(EventListeners(vec!())), + Vacant(entry) => entry.insert(EventListeners(vec![])), }; - let idx = entries.iter().position(|ref entry| { - match entry.listener { - EventListenerType::Inline(_) => true, - _ => false, - } + let idx = entries.iter().position(|ref entry| match entry.listener { + EventListenerType::Inline(_) => true, + _ => false, }); match idx { - Some(idx) => { - entries[idx].listener = - EventListenerType::Inline(listener.unwrap_or(InlineEventListener::Null)); - } + Some(idx) => match listener { + // Replace if there's something to replace with, + // but remove entirely if there isn't. + Some(listener) => { + entries[idx].listener = EventListenerType::Inline(listener); + }, + None => { + entries.remove(idx); + }, + }, None => { if let Some(listener) = listener { entries.push(EventListenerEntry { phase: ListenerPhase::Bubbling, listener: EventListenerType::Inline(listener), + once: false, }); } - } + }, + } + } + + pub fn remove_listener_if_once(&self, ty: &Atom, listener: &Rc<EventListener>) { + let mut handlers = self.handlers.borrow_mut(); + + let listener = EventListenerType::Additive(listener.clone()); + for entries in handlers.get_mut(ty) { + entries.drain_filter(|e| e.listener == listener && e.once); } } fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> { let mut handlers = self.handlers.borrow_mut(); - handlers.get_mut(ty).and_then(|entry| entry.get_inline_listener(self, ty)) + handlers + .get_mut(ty) + .and_then(|entry| entry.get_inline_listener(self, ty)) } /// Store the raw uncompiled event handler for on-demand compilation later. - /// https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handler-content-attributes-3 - pub fn set_event_handler_uncompiled(&self, - url: ServoUrl, - line: usize, - ty: &str, - source: DOMString) { + /// <https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handler-content-attributes-3> + pub fn set_event_handler_uncompiled( + &self, + url: ServoUrl, + line: usize, + ty: &str, + source: DOMString, + ) { let handler = InternalRawUncompiledHandler { source: source, line: line, url: url, }; - self.set_inline_event_listener(Atom::from(ty), - Some(InlineEventListener::Uncompiled(handler))); + self.set_inline_event_listener( + Atom::from(ty), + Some(InlineEventListener::Uncompiled(handler)), + ); } // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler + // step 3 #[allow(unsafe_code)] - fn get_compiled_event_handler(&self, - handler: InternalRawUncompiledHandler, - ty: &Atom) - -> Option<CommonEventHandler> { - // Step 1.1 + fn get_compiled_event_handler( + &self, + handler: InternalRawUncompiledHandler, + ty: &Atom, + ) -> Option<CommonEventHandler> { + // Step 3.1 let element = self.downcast::<Element>(); let document = match element { Some(element) => document_from_node(element), None => self.downcast::<Window>().unwrap().Document(), }; - // Step 1.2 + // Step 3.2 if !document.is_scripting_enabled() { return None; } - // Step 1.3 + // Step 3.3 let body: Vec<u16> = handler.source.encode_utf16().collect(); - // TODO step 1.5 (form owner) + // Step 3.4 is handler.line + + // Step 3.5 + let form_owner = element + .and_then(|e| e.as_maybe_form_control()) + .and_then(|f| f.form_owner()); - // Step 1.6 + // Step 3.6 TODO: settings objects not implemented + + // Step 3.7 is written as though we call the parser separately + // from the compiler; since we just call CompileFunction with + // source text, we handle parse errors later + + // Step 3.8 TODO: settings objects not implemented let window = document.window(); + let _ac = enter_realm(&*window); + + // Step 3.9 - let url_serialized = CString::new(handler.url.to_string()).unwrap(); - let name = CString::new(&**ty).unwrap(); + let name = CString::new(format!("on{}", &**ty)).unwrap(); - static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char]; - static mut ERROR_ARG_NAMES: [*const c_char; 5] = [b"event\0" as *const u8 as *const c_char, - b"source\0" as *const u8 as *const c_char, - b"lineno\0" as *const u8 as *const c_char, - b"colno\0" as *const u8 as *const c_char, - b"error\0" as *const u8 as *const c_char]; - // step 10 + // Step 3.9, subsection ParameterList + const ARG_NAMES: &[*const c_char] = &[b"event\0" as *const u8 as *const c_char]; + const ERROR_ARG_NAMES: &[*const c_char] = &[ + b"event\0" as *const u8 as *const c_char, + b"source\0" as *const u8 as *const c_char, + b"lineno\0" as *const u8 as *const c_char, + b"colno\0" as *const u8 as *const c_char, + b"error\0" as *const u8 as *const c_char, + ]; let is_error = ty == &atom!("error") && self.is::<Window>(); - let args = unsafe { - if is_error { - &ERROR_ARG_NAMES[..] - } else { - &ARG_NAMES[..] - } - }; + let args = if is_error { ERROR_ARG_NAMES } else { ARG_NAMES }; let cx = window.get_cx(); - let options = CompileOptionsWrapper::new(cx, url_serialized.as_ptr(), handler.line as u32); - // TODO step 1.10.1-3 (document, form owner, element in scope chain) - - let scopechain = AutoObjectVectorWrapper::new(cx); - - let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get()); - rooted!(in(cx) let mut handler = ptr::null_mut()); - let rv = unsafe { - CompileFunction(cx, - scopechain.ptr, - options.ptr, - name.as_ptr(), - args.len() as u32, - args.as_ptr(), - body.as_ptr(), - body.len() as size_t, - handler.handle_mut()) + let options = unsafe { + CompileOptionsWrapper::new(*cx, &handler.url.to_string(), handler.line as u32) }; - if !rv || handler.get().is_null() { - // Step 1.8.2 + + // Step 3.9, subsection Scope steps 1-6 + let scopechain = RootedObjectVectorWrapper::new(*cx); + + if let Some(element) = element { + scopechain.append(document.reflector().get_jsobject().get()); + if let Some(form_owner) = form_owner { + scopechain.append(form_owner.reflector().get_jsobject().get()); + } + scopechain.append(element.reflector().get_jsobject().get()); + } + + rooted!(in(*cx) let mut handler = unsafe { + CompileFunction( + *cx, + scopechain.handle(), + options.ptr, + name.as_ptr(), + args.len() as u32, + args.as_ptr(), + &mut transform_u16_to_source_text(&body), + ) + }); + if handler.get().is_null() { + // Step 3.7 unsafe { - let _ac = JSAutoCompartment::new(cx, self.reflector().get_jsobject().get()); + let ar = enter_realm(&*self); // FIXME(#13152): dispatch error event. - report_pending_exception(cx, false); + report_pending_exception(*cx, false, InRealm::Entered(&ar)); } - // Step 1.8.1 / 1.8.3 return None; } - // TODO step 1.11-13 + // Step 3.10 happens when we drop _ac + + // TODO Step 3.11 + + // Step 3.12 let funobj = unsafe { JS_GetFunctionObject(handler.get()) }; assert!(!funobj.is_null()); // Step 1.14 if is_error { - Some(CommonEventHandler::ErrorEventHandler(OnErrorEventHandlerNonNull::new(cx, funobj))) + Some(CommonEventHandler::ErrorEventHandler(unsafe { + OnErrorEventHandlerNonNull::new(cx, funobj) + })) } else { if ty == &atom!("beforeunload") { - Some(CommonEventHandler::BeforeUnloadEventHandler( - OnBeforeUnloadEventHandlerNonNull::new(cx, funobj))) + Some(CommonEventHandler::BeforeUnloadEventHandler(unsafe { + OnBeforeUnloadEventHandlerNonNull::new(cx, funobj) + })) } else { - Some(CommonEventHandler::EventHandler(EventHandlerNonNull::new(cx, funobj))) + Some(CommonEventHandler::EventHandler(unsafe { + EventHandlerNonNull::new(cx, funobj) + })) } } } - pub fn set_event_handler_common<T: CallbackContainer>( - &self, ty: &str, listener: Option<Rc<T>>) + #[allow(unsafe_code)] + pub fn set_event_handler_common<T: CallbackContainer>(&self, ty: &str, listener: Option<Rc<T>>) + where + T: CallbackContainer, { let cx = self.global().get_cx(); - let event_listener = listener.map(|listener| - InlineEventListener::Compiled( - CommonEventHandler::EventHandler( - EventHandlerNonNull::new(cx, listener.callback())))); + let event_listener = listener.map(|listener| { + InlineEventListener::Compiled(CommonEventHandler::EventHandler(unsafe { + EventHandlerNonNull::new(cx, listener.callback()) + })) + }); self.set_inline_event_listener(Atom::from(ty), event_listener); } - pub fn set_error_event_handler<T: CallbackContainer>( - &self, ty: &str, listener: Option<Rc<T>>) + #[allow(unsafe_code)] + pub fn set_error_event_handler<T: CallbackContainer>(&self, ty: &str, listener: Option<Rc<T>>) + where + T: CallbackContainer, { let cx = self.global().get_cx(); - let event_listener = listener.map(|listener| - InlineEventListener::Compiled( - CommonEventHandler::ErrorEventHandler( - OnErrorEventHandlerNonNull::new(cx, listener.callback())))); + let event_listener = listener.map(|listener| { + InlineEventListener::Compiled(CommonEventHandler::ErrorEventHandler(unsafe { + OnErrorEventHandlerNonNull::new(cx, listener.callback()) + })) + }); self.set_inline_event_listener(Atom::from(ty), event_listener); } - pub fn set_beforeunload_event_handler<T: CallbackContainer>(&self, ty: &str, - listener: Option<Rc<T>>) { + #[allow(unsafe_code)] + pub fn set_beforeunload_event_handler<T: CallbackContainer>( + &self, + ty: &str, + listener: Option<Rc<T>>, + ) where + T: CallbackContainer, + { let cx = self.global().get_cx(); - let event_listener = listener.map(|listener| - InlineEventListener::Compiled( - CommonEventHandler::BeforeUnloadEventHandler( - OnBeforeUnloadEventHandlerNonNull::new(cx, listener.callback()))) - ); + let event_listener = listener.map(|listener| { + InlineEventListener::Compiled(CommonEventHandler::BeforeUnloadEventHandler(unsafe { + OnBeforeUnloadEventHandlerNonNull::new(cx, listener.callback()) + })) + }); self.set_inline_event_listener(Atom::from(ty), event_listener); } @@ -496,8 +640,9 @@ impl EventTarget { let cx = self.global().get_cx(); let listener = self.get_inline_event_listener(&Atom::from(ty)); unsafe { - listener.map(|listener| - CallbackContainer::new(cx, listener.parent().callback_holder().get())) + listener.map(|listener| { + CallbackContainer::new(cx, listener.parent().callback_holder().get()) + }) } } @@ -506,51 +651,51 @@ impl EventTarget { } // https://dom.spec.whatwg.org/#concept-event-fire - pub fn fire_event(&self, name: Atom) -> Root<Event> { - self.fire_event_with_params(name, - EventBubbles::DoesNotBubble, - EventCancelable::NotCancelable) + pub fn fire_event(&self, name: Atom) -> DomRoot<Event> { + self.fire_event_with_params( + name, + EventBubbles::DoesNotBubble, + EventCancelable::NotCancelable, + ) } // https://dom.spec.whatwg.org/#concept-event-fire - pub fn fire_bubbling_event(&self, name: Atom) -> Root<Event> { - self.fire_event_with_params(name, - EventBubbles::Bubbles, - EventCancelable::NotCancelable) + pub fn fire_bubbling_event(&self, name: Atom) -> DomRoot<Event> { + self.fire_event_with_params(name, EventBubbles::Bubbles, EventCancelable::NotCancelable) } // https://dom.spec.whatwg.org/#concept-event-fire - pub fn fire_cancelable_event(&self, name: Atom) -> Root<Event> { - self.fire_event_with_params(name, - EventBubbles::DoesNotBubble, - EventCancelable::Cancelable) + pub fn fire_cancelable_event(&self, name: Atom) -> DomRoot<Event> { + self.fire_event_with_params( + name, + EventBubbles::DoesNotBubble, + EventCancelable::Cancelable, + ) } // https://dom.spec.whatwg.org/#concept-event-fire - pub fn fire_bubbling_cancelable_event(&self, name: Atom) -> Root<Event> { - self.fire_event_with_params(name, - EventBubbles::Bubbles, - EventCancelable::Cancelable) + pub fn fire_bubbling_cancelable_event(&self, name: Atom) -> DomRoot<Event> { + self.fire_event_with_params(name, EventBubbles::Bubbles, EventCancelable::Cancelable) } // https://dom.spec.whatwg.org/#concept-event-fire - pub fn fire_event_with_params(&self, - name: Atom, - bubbles: EventBubbles, - cancelable: EventCancelable) - -> Root<Event> { + pub fn fire_event_with_params( + &self, + name: Atom, + bubbles: EventBubbles, + cancelable: EventCancelable, + ) -> DomRoot<Event> { let event = Event::new(&self.global(), name, bubbles, cancelable); event.fire(self); event } -} - -impl EventTargetMethods for EventTarget { // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener - fn AddEventListener(&self, - ty: DOMString, - listener: Option<Rc<EventListener>>, - capture: bool) { + pub fn add_event_listener( + &self, + ty: DOMString, + listener: Option<Rc<EventListener>>, + options: AddEventListenerOptions, + ) { let listener = match listener { Some(l) => l, None => return, @@ -558,13 +703,18 @@ impl EventTargetMethods for EventTarget { let mut handlers = self.handlers.borrow_mut(); let entry = match handlers.entry(Atom::from(ty)) { Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(EventListeners(vec!())), + Vacant(entry) => entry.insert(EventListeners(vec![])), }; - let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling }; + let phase = if options.parent.capture { + ListenerPhase::Capturing + } else { + ListenerPhase::Bubbling + }; let new_entry = EventListenerEntry { phase: phase, - listener: EventListenerType::Additive(listener) + listener: EventListenerType::Additive(listener), + once: options.once, }; if !entry.contains(&new_entry) { entry.push(new_entry); @@ -572,10 +722,12 @@ impl EventTargetMethods for EventTarget { } // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener - fn RemoveEventListener(&self, - ty: DOMString, - listener: Option<Rc<EventListener>>, - capture: bool) { + pub fn remove_event_listener( + &self, + ty: DOMString, + listener: Option<Rc<EventListener>>, + options: EventListenerOptions, + ) { let ref listener = match listener { Some(l) => l, None => return, @@ -583,16 +735,43 @@ impl EventTargetMethods for EventTarget { let mut handlers = self.handlers.borrow_mut(); let entry = handlers.get_mut(&Atom::from(ty)); for entry in entry { - let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling }; + let phase = if options.capture { + ListenerPhase::Capturing + } else { + ListenerPhase::Bubbling + }; let old_entry = EventListenerEntry { phase: phase, - listener: EventListenerType::Additive(listener.clone()) + listener: EventListenerType::Additive(listener.clone()), + once: false, }; if let Some(position) = entry.iter().position(|e| *e == old_entry) { entry.remove(position); } } } +} + +impl EventTargetMethods for EventTarget { + // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener + fn AddEventListener( + &self, + ty: DOMString, + listener: Option<Rc<EventListener>>, + options: AddEventListenerOptionsOrBoolean, + ) { + self.add_event_listener(ty, listener, options.into()) + } + + // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener + fn RemoveEventListener( + &self, + ty: DOMString, + listener: Option<Rc<EventListener>>, + options: EventListenerOptionsOrBoolean, + ) { + self.remove_event_listener(ty, listener, options.into()) + } // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent fn DispatchEvent(&self, event: &Event) -> Fallible<bool> { @@ -602,13 +781,34 @@ impl EventTargetMethods for EventTarget { event.set_trusted(false); Ok(match self.dispatch_event(event) { EventStatus::Canceled => false, - EventStatus::NotCanceled => true + EventStatus::NotCanceled => true, }) } } impl VirtualMethods for EventTarget { - fn super_type(&self) -> Option<&VirtualMethods> { + fn super_type(&self) -> Option<&dyn VirtualMethods> { None } } + +impl From<AddEventListenerOptionsOrBoolean> for AddEventListenerOptions { + fn from(options: AddEventListenerOptionsOrBoolean) -> Self { + match options { + AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options, + AddEventListenerOptionsOrBoolean::Boolean(capture) => Self { + parent: EventListenerOptions { capture }, + once: false, + }, + } + } +} + +impl From<EventListenerOptionsOrBoolean> for EventListenerOptions { + fn from(options: EventListenerOptionsOrBoolean) -> Self { + match options { + EventListenerOptionsOrBoolean::EventListenerOptions(options) => options, + EventListenerOptionsOrBoolean::Boolean(capture) => Self { capture }, + } + } +} |