mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Auto merge of #8491 - jdm:lazycompile, r=Ms2ger
Compile raw inline event handlers lazily. Resolves #8489. Per https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler and https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes:event-handlers-2 . <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8491) <!-- Reviewable:end -->
This commit is contained in:
commit
5942e9e3cb
11 changed files with 323 additions and 67 deletions
|
@ -255,6 +255,15 @@ impl<A: JSTraceable, B: JSTraceable> JSTraceable for (A, B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<A: JSTraceable, B: JSTraceable, C: JSTraceable> JSTraceable for (A, B, C) {
|
||||||
|
#[inline]
|
||||||
|
fn trace(&self, trc: *mut JSTracer) {
|
||||||
|
let (ref a, ref b, ref c) = *self;
|
||||||
|
a.trace(trc);
|
||||||
|
b.trace(trc);
|
||||||
|
c.trace(trc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool, Uuid);
|
no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool, Uuid);
|
||||||
no_jsmanaged_fields!(usize, u8, u16, u32, u64);
|
no_jsmanaged_fields!(usize, u8, u16, u32, u64);
|
||||||
|
|
|
@ -11,7 +11,7 @@ use dom::bindings::js::{JS, Root, RootedReference};
|
||||||
use dom::bindings::reflector::Reflectable;
|
use dom::bindings::reflector::Reflectable;
|
||||||
use dom::bindings::trace::RootedVec;
|
use dom::bindings::trace::RootedVec;
|
||||||
use dom::event::{Event, EventPhase};
|
use dom::event::{Event, EventPhase};
|
||||||
use dom::eventtarget::{EventListenerType, EventTarget, ListenerPhase};
|
use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
|
||||||
use dom::node::Node;
|
use dom::node::Node;
|
||||||
use dom::virtualmethods::vtable_for;
|
use dom::virtualmethods::vtable_for;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
@ -36,7 +36,7 @@ impl Drop for AutoDOMEventMarker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_event(window: Option<&Window>, listener: &EventListenerType,
|
fn handle_event(window: Option<&Window>, listener: &CompiledEventListener,
|
||||||
current_target: &EventTarget, event: &Event) {
|
current_target: &EventTarget, event: &Event) {
|
||||||
let _marker;
|
let _marker;
|
||||||
if let Some(window) = window {
|
if let Some(window) = window {
|
||||||
|
|
|
@ -9,20 +9,22 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
|
||||||
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
|
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
|
||||||
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
|
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
|
||||||
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
|
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::codegen::UnionTypes::EventOrString;
|
use dom::bindings::codegen::UnionTypes::EventOrString;
|
||||||
use dom::bindings::error::{Error, Fallible, report_pending_exception};
|
use dom::bindings::error::{Error, Fallible, report_pending_exception};
|
||||||
use dom::bindings::inheritance::{Castable, EventTargetTypeId};
|
use dom::bindings::inheritance::{Castable, EventTargetTypeId};
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::bindings::reflector::{Reflectable, Reflector};
|
use dom::bindings::reflector::{Reflectable, Reflector};
|
||||||
|
use dom::element::Element;
|
||||||
use dom::errorevent::ErrorEvent;
|
use dom::errorevent::ErrorEvent;
|
||||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||||
use dom::eventdispatcher::dispatch_event;
|
use dom::eventdispatcher::dispatch_event;
|
||||||
|
use dom::node::document_from_node;
|
||||||
use dom::virtualmethods::VirtualMethods;
|
use dom::virtualmethods::VirtualMethods;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue};
|
use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue, RootedFunction};
|
||||||
use js::jsapi::{HandleObject, JSContext, RootedFunction};
|
|
||||||
use js::jsapi::{JSAutoCompartment, JSAutoRequest};
|
use js::jsapi::{JSAutoCompartment, JSAutoRequest};
|
||||||
use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
|
use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
|
||||||
use libc::{c_char, size_t};
|
use libc::{c_char, size_t};
|
||||||
|
@ -31,6 +33,8 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
use std::mem;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{intrinsics, ptr};
|
use std::{intrinsics, ptr};
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
|
@ -94,10 +98,49 @@ impl EventTargetTypeId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#internal-raw-uncompiled-handler
|
||||||
#[derive(JSTraceable, Clone, PartialEq)]
|
#[derive(JSTraceable, Clone, PartialEq)]
|
||||||
pub enum EventListenerType {
|
pub struct InternalRawUncompiledHandler {
|
||||||
|
source: DOMString,
|
||||||
|
url: Url,
|
||||||
|
line: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A representation of an event handler, either compiled or uncompiled raw source, or null.
|
||||||
|
#[derive(JSTraceable, PartialEq, Clone)]
|
||||||
|
pub enum InlineEventListener {
|
||||||
|
Uncompiled(InternalRawUncompiledHandler),
|
||||||
|
Compiled(CommonEventHandler),
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
match mem::replace(self, InlineEventListener::Null) {
|
||||||
|
InlineEventListener::Null => None,
|
||||||
|
InlineEventListener::Uncompiled(handler) => {
|
||||||
|
let result = owner.get_compiled_event_handler(handler, ty);
|
||||||
|
if let Some(ref compiled) = result {
|
||||||
|
*self = InlineEventListener::Compiled(compiled.clone());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
InlineEventListener::Compiled(handler) => {
|
||||||
|
*self = InlineEventListener::Compiled(handler.clone());
|
||||||
|
Some(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(JSTraceable, Clone, PartialEq)]
|
||||||
|
enum EventListenerType {
|
||||||
Additive(Rc<EventListener>),
|
Additive(Rc<EventListener>),
|
||||||
Inline(CommonEventHandler),
|
Inline(InlineEventListener),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeapSizeOf for EventListenerType {
|
impl HeapSizeOf for EventListenerType {
|
||||||
|
@ -108,6 +151,26 @@ impl HeapSizeOf for EventListenerType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventListenerType {
|
impl EventListenerType {
|
||||||
|
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())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A representation of an EventListener/EventHandler object that has previously
|
||||||
|
/// been compiled successfully, if applicable.
|
||||||
|
pub enum CompiledEventListener {
|
||||||
|
Listener(Rc<EventListener>),
|
||||||
|
Handler(CommonEventHandler),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompiledEventListener {
|
||||||
// https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm
|
// https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm
|
||||||
pub fn call_or_handle_event<T: Reflectable>(&self,
|
pub fn call_or_handle_event<T: Reflectable>(&self,
|
||||||
object: &T,
|
object: &T,
|
||||||
|
@ -115,10 +178,10 @@ impl EventListenerType {
|
||||||
exception_handle: ExceptionHandling) {
|
exception_handle: ExceptionHandling) {
|
||||||
// Step 3
|
// Step 3
|
||||||
match *self {
|
match *self {
|
||||||
EventListenerType::Additive(ref listener) => {
|
CompiledEventListener::Listener(ref listener) => {
|
||||||
let _ = listener.HandleEvent_(object, event, exception_handle);
|
let _ = listener.HandleEvent_(object, event, exception_handle);
|
||||||
},
|
},
|
||||||
EventListenerType::Inline(ref handler) => {
|
CompiledEventListener::Handler(ref handler) => {
|
||||||
match *handler {
|
match *handler {
|
||||||
CommonEventHandler::ErrorEventHandler(ref handler) => {
|
CommonEventHandler::ErrorEventHandler(ref handler) => {
|
||||||
if let Some(event) = event.downcast::<ErrorEvent>() {
|
if let Some(event) = event.downcast::<ErrorEvent>() {
|
||||||
|
@ -152,15 +215,61 @@ impl EventListenerType {
|
||||||
|
|
||||||
#[derive(JSTraceable, Clone, PartialEq, HeapSizeOf)]
|
#[derive(JSTraceable, Clone, PartialEq, HeapSizeOf)]
|
||||||
#[privatize]
|
#[privatize]
|
||||||
pub struct EventListenerEntry {
|
/// A listener in a collection of event listeners.
|
||||||
|
struct EventListenerEntry {
|
||||||
phase: ListenerPhase,
|
phase: ListenerPhase,
|
||||||
listener: EventListenerType
|
listener: EventListenerType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(JSTraceable, HeapSizeOf)]
|
||||||
|
/// A mix of potentially uncompiled and compiled event listeners of the same type.
|
||||||
|
struct EventListeners(Vec<EventListenerEntry>);
|
||||||
|
|
||||||
|
impl Deref for EventListeners {
|
||||||
|
type Target = Vec<EventListenerEntry>;
|
||||||
|
fn deref(&self) -> &Vec<EventListenerEntry> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for EventListeners {
|
||||||
|
fn deref_mut(&mut self) -> &mut Vec<EventListenerEntry> {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
for entry in &mut self.0 {
|
||||||
|
if let EventListenerType::Inline(ref mut inline) = entry.listener {
|
||||||
|
// Step 1.1-1.8 and Step 2
|
||||||
|
return inline.get_compiled_handler(owner, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct EventTarget {
|
pub struct EventTarget {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
handlers: DOMRefCell<HashMap<Atom, Vec<EventListenerEntry>, BuildHasherDefault<FnvHasher>>>,
|
handlers: DOMRefCell<HashMap<Atom, EventListeners, BuildHasherDefault<FnvHasher>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventTarget {
|
impl EventTarget {
|
||||||
|
@ -171,17 +280,16 @@ impl EventTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_listeners(&self, type_: &Atom) -> Option<Vec<EventListenerType>> {
|
pub fn get_listeners(&self, type_: &Atom) -> Option<Vec<CompiledEventListener>> {
|
||||||
self.handlers.borrow().get(type_).map(|listeners| {
|
self.handlers.borrow_mut().get_mut(type_).map(|listeners| {
|
||||||
listeners.iter().map(|entry| entry.listener.clone()).collect()
|
listeners.get_listeners(None, self, type_)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_listeners_for(&self, type_: &Atom, desired_phase: ListenerPhase)
|
pub fn get_listeners_for(&self, type_: &Atom, desired_phase: ListenerPhase)
|
||||||
-> Option<Vec<EventListenerType>> {
|
-> Option<Vec<CompiledEventListener>> {
|
||||||
self.handlers.borrow().get(type_).map(|listeners| {
|
self.handlers.borrow_mut().get_mut(type_).map(|listeners| {
|
||||||
let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
|
listeners.get_listeners(Some(desired_phase), self, type_)
|
||||||
filtered.map(|entry| entry.listener.clone()).collect()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,13 +303,14 @@ impl EventTarget {
|
||||||
dispatch_event(self, None, event)
|
dispatch_event(self, None, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handlers-11
|
||||||
pub fn set_inline_event_listener(&self,
|
pub fn set_inline_event_listener(&self,
|
||||||
ty: Atom,
|
ty: Atom,
|
||||||
listener: Option<CommonEventHandler>) {
|
listener: Option<InlineEventListener>) {
|
||||||
let mut handlers = self.handlers.borrow_mut();
|
let mut handlers = self.handlers.borrow_mut();
|
||||||
let entries = match handlers.entry(ty) {
|
let entries = match handlers.entry(ty) {
|
||||||
Occupied(entry) => entry.into_mut(),
|
Occupied(entry) => entry.into_mut(),
|
||||||
Vacant(entry) => entry.insert(vec!()),
|
Vacant(entry) => entry.insert(EventListeners(vec!())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let idx = entries.iter().position(|ref entry| {
|
let idx = entries.iter().position(|ref entry| {
|
||||||
|
@ -213,12 +322,8 @@ impl EventTarget {
|
||||||
|
|
||||||
match idx {
|
match idx {
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
match listener {
|
entries[idx].listener =
|
||||||
Some(listener) => entries[idx].listener = EventListenerType::Inline(listener),
|
EventListenerType::Inline(listener.unwrap_or(InlineEventListener::Null));
|
||||||
None => {
|
|
||||||
entries.remove(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if listener.is_some() {
|
if listener.is_some() {
|
||||||
|
@ -231,28 +336,52 @@ impl EventTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
|
fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
|
||||||
let handlers = self.handlers.borrow();
|
let mut handlers = self.handlers.borrow_mut();
|
||||||
let entries = handlers.get(ty);
|
handlers.get_mut(ty).and_then(|entry| entry.get_inline_listener(self, ty))
|
||||||
entries.and_then(|entries| entries.iter().filter_map(|entry| {
|
|
||||||
match entry.listener {
|
|
||||||
EventListenerType::Inline(ref handler) => Some(handler.clone()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}).next())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
/// Store the raw uncompiled event handler for on-demand compilation later.
|
||||||
// https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
|
/// https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handler-content-attributes-3
|
||||||
pub fn set_event_handler_uncompiled(&self,
|
pub fn set_event_handler_uncompiled(&self,
|
||||||
cx: *mut JSContext,
|
url: Url,
|
||||||
url: Url,
|
line: usize,
|
||||||
scope: HandleObject,
|
ty: &str,
|
||||||
ty: &str,
|
source: DOMString) {
|
||||||
source: DOMString) {
|
let handler = InternalRawUncompiledHandler {
|
||||||
let url = CString::new(url.serialize()).unwrap();
|
source: source,
|
||||||
let name = CString::new(ty).unwrap();
|
line: line,
|
||||||
let lineno = 0; //XXXjdm need to get a real number here
|
url: url,
|
||||||
|
};
|
||||||
|
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
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn get_compiled_event_handler(&self,
|
||||||
|
handler: InternalRawUncompiledHandler,
|
||||||
|
ty: &Atom)
|
||||||
|
-> Option<CommonEventHandler> {
|
||||||
|
// Step 1.1
|
||||||
|
let element = self.downcast::<Element>();
|
||||||
|
let document = match element {
|
||||||
|
Some(element) => document_from_node(element),
|
||||||
|
None => self.downcast::<Window>().unwrap().Document(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO step 1.2 (browsing context/scripting enabled)
|
||||||
|
|
||||||
|
// Step 1.3
|
||||||
|
let body: Vec<u16> = handler.source.utf16_units().collect();
|
||||||
|
|
||||||
|
// TODO step 1.5 (form owner)
|
||||||
|
|
||||||
|
// Step 1.6
|
||||||
|
let window = document.window();
|
||||||
|
|
||||||
|
let url_serialized = CString::new(handler.url.serialize()).unwrap();
|
||||||
|
let name = CString::new(&**ty).unwrap();
|
||||||
|
|
||||||
static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char];
|
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,
|
static mut ERROR_ARG_NAMES: [*const c_char; 5] = [b"event\0" as *const u8 as *const c_char,
|
||||||
|
@ -261,7 +390,7 @@ impl EventTarget {
|
||||||
b"colno\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];
|
b"error\0" as *const u8 as *const c_char];
|
||||||
// step 10
|
// step 10
|
||||||
let is_error = ty == "error" && self.is::<Window>();
|
let is_error = ty == &Atom::from("error") && self.is::<Window>();
|
||||||
let args = unsafe {
|
let args = unsafe {
|
||||||
if is_error {
|
if is_error {
|
||||||
&ERROR_ARG_NAMES[..]
|
&ERROR_ARG_NAMES[..]
|
||||||
|
@ -270,12 +399,14 @@ impl EventTarget {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let source: Vec<u16> = source.utf16_units().collect();
|
let cx = window.get_cx();
|
||||||
let options = CompileOptionsWrapper::new(cx, url.as_ptr(), lineno);
|
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 scopechain = AutoObjectVectorWrapper::new(cx);
|
||||||
|
|
||||||
let _ar = JSAutoRequest::new(cx);
|
let _ar = JSAutoRequest::new(cx);
|
||||||
let _ac = JSAutoCompartment::new(cx, scope.get());
|
let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get());
|
||||||
let mut handler = RootedFunction::new(cx, ptr::null_mut());
|
let mut handler = RootedFunction::new(cx, ptr::null_mut());
|
||||||
let rv = unsafe {
|
let rv = unsafe {
|
||||||
CompileFunction(cx,
|
CompileFunction(cx,
|
||||||
|
@ -284,21 +415,25 @@ impl EventTarget {
|
||||||
name.as_ptr(),
|
name.as_ptr(),
|
||||||
args.len() as u32,
|
args.len() as u32,
|
||||||
args.as_ptr(),
|
args.as_ptr(),
|
||||||
source.as_ptr(),
|
body.as_ptr(),
|
||||||
source.len() as size_t,
|
body.len() as size_t,
|
||||||
handler.handle_mut())
|
handler.handle_mut())
|
||||||
};
|
};
|
||||||
if !rv || handler.ptr.is_null() {
|
if !rv || handler.ptr.is_null() {
|
||||||
|
// Step 1.8.2
|
||||||
report_pending_exception(cx, self.reflector().get_jsobject().get());
|
report_pending_exception(cx, self.reflector().get_jsobject().get());
|
||||||
return;
|
// Step 1.8.1 / 1.8.3
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO step 1.11-13
|
||||||
let funobj = unsafe { JS_GetFunctionObject(handler.ptr) };
|
let funobj = unsafe { JS_GetFunctionObject(handler.ptr) };
|
||||||
assert!(!funobj.is_null());
|
assert!(!funobj.is_null());
|
||||||
|
// Step 1.14
|
||||||
if is_error {
|
if is_error {
|
||||||
self.set_error_event_handler(ty, Some(OnErrorEventHandlerNonNull::new(funobj)));
|
Some(CommonEventHandler::ErrorEventHandler(OnErrorEventHandlerNonNull::new(funobj)))
|
||||||
} else {
|
} else {
|
||||||
self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)));
|
Some(CommonEventHandler::EventHandler(EventHandlerNonNull::new(funobj)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,8 +441,9 @@ impl EventTarget {
|
||||||
&self, ty: &str, listener: Option<Rc<T>>)
|
&self, ty: &str, listener: Option<Rc<T>>)
|
||||||
{
|
{
|
||||||
let event_listener = listener.map(|listener|
|
let event_listener = listener.map(|listener|
|
||||||
CommonEventHandler::EventHandler(
|
InlineEventListener::Compiled(
|
||||||
EventHandlerNonNull::new(listener.callback())));
|
CommonEventHandler::EventHandler(
|
||||||
|
EventHandlerNonNull::new(listener.callback()))));
|
||||||
self.set_inline_event_listener(Atom::from(ty), event_listener);
|
self.set_inline_event_listener(Atom::from(ty), event_listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,8 +451,9 @@ impl EventTarget {
|
||||||
&self, ty: &str, listener: Option<Rc<T>>)
|
&self, ty: &str, listener: Option<Rc<T>>)
|
||||||
{
|
{
|
||||||
let event_listener = listener.map(|listener|
|
let event_listener = listener.map(|listener|
|
||||||
CommonEventHandler::ErrorEventHandler(
|
InlineEventListener::Compiled(
|
||||||
OnErrorEventHandlerNonNull::new(listener.callback())));
|
CommonEventHandler::ErrorEventHandler(
|
||||||
|
OnErrorEventHandlerNonNull::new(listener.callback()))));
|
||||||
self.set_inline_event_listener(Atom::from(ty), event_listener);
|
self.set_inline_event_listener(Atom::from(ty), event_listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +496,7 @@ impl EventTargetMethods for EventTarget {
|
||||||
let mut handlers = self.handlers.borrow_mut();
|
let mut handlers = self.handlers.borrow_mut();
|
||||||
let entry = match handlers.entry(Atom::from(&*ty)) {
|
let entry = match handlers.entry(Atom::from(&*ty)) {
|
||||||
Occupied(entry) => entry.into_mut(),
|
Occupied(entry) => entry.into_mut(),
|
||||||
Vacant(entry) => entry.insert(vec!()),
|
Vacant(entry) => entry.insert(EventListeners(vec!())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
|
let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
|
||||||
|
|
|
@ -162,9 +162,6 @@ impl VirtualMethods for HTMLBodyElement {
|
||||||
let do_super_mutate = match (attr.local_name(), mutation) {
|
let do_super_mutate = match (attr.local_name(), mutation) {
|
||||||
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
|
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
let (cx, url, reflector) = (window.get_cx(),
|
|
||||||
window.get_url(),
|
|
||||||
window.reflector().get_jsobject());
|
|
||||||
// https://html.spec.whatwg.org/multipage/
|
// https://html.spec.whatwg.org/multipage/
|
||||||
// #event-handlers-on-elements,-document-objects,-and-window-objects:event-handlers-3
|
// #event-handlers-on-elements,-document-objects,-and-window-objects:event-handlers-3
|
||||||
match name {
|
match name {
|
||||||
|
@ -175,7 +172,9 @@ impl VirtualMethods for HTMLBodyElement {
|
||||||
&atom!("onresize") | &atom!("onunload") | &atom!("onerror")
|
&atom!("onresize") | &atom!("onunload") | &atom!("onerror")
|
||||||
=> {
|
=> {
|
||||||
let evtarget = window.upcast::<EventTarget>(); // forwarded event
|
let evtarget = window.upcast::<EventTarget>(); // forwarded event
|
||||||
evtarget.set_event_handler_uncompiled(cx, url, reflector,
|
let source_line = 1; //TODO(#9604) obtain current JS execution line
|
||||||
|
evtarget.set_event_handler_uncompiled(window.get_url(),
|
||||||
|
source_line,
|
||||||
&name[2..],
|
&name[2..],
|
||||||
DOMString::from((**attr.value()).to_owned()));
|
DOMString::from((**attr.value()).to_owned()));
|
||||||
false
|
false
|
||||||
|
|
|
@ -453,12 +453,10 @@ impl VirtualMethods for HTMLElement {
|
||||||
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
match (attr.local_name(), mutation) {
|
match (attr.local_name(), mutation) {
|
||||||
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
|
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
|
||||||
let window = window_from_node(self);
|
|
||||||
let (cx, url, reflector) = (window.get_cx(),
|
|
||||||
window.get_url(),
|
|
||||||
window.reflector().get_jsobject());
|
|
||||||
let evtarget = self.upcast::<EventTarget>();
|
let evtarget = self.upcast::<EventTarget>();
|
||||||
evtarget.set_event_handler_uncompiled(cx, url, reflector,
|
let source_line = 1; //TODO(#9604) get current JS execution line
|
||||||
|
evtarget.set_event_handler_uncompiled(window_from_node(self).get_url(),
|
||||||
|
source_line,
|
||||||
&name[2..],
|
&name[2..],
|
||||||
// FIXME(ajeffrey): Convert directly from AttrValue to DOMString
|
// FIXME(ajeffrey): Convert directly from AttrValue to DOMString
|
||||||
DOMString::from(&**attr.value()));
|
DOMString::from(&**attr.value()));
|
||||||
|
|
|
@ -33719,6 +33719,18 @@
|
||||||
"deleted": [],
|
"deleted": [],
|
||||||
"items": {
|
"items": {
|
||||||
"testharness": {
|
"testharness": {
|
||||||
|
"html/webappapis/scripting/events/inline-event-handler-ordering.html": [
|
||||||
|
{
|
||||||
|
"path": "html/webappapis/scripting/events/inline-event-handler-ordering.html",
|
||||||
|
"url": "/html/webappapis/scripting/events/inline-event-handler-ordering.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.html": [
|
||||||
|
{
|
||||||
|
"path": "html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.html",
|
||||||
|
"url": "/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
"websockets/Send-data.worker.js": [
|
"websockets/Send-data.worker.js": [
|
||||||
{
|
{
|
||||||
"path": "websockets/Send-data.worker.js",
|
"path": "websockets/Send-data.worker.js",
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
[invalid-uncompiled-raw-handler-compiled-once.html]
|
||||||
|
type: testharness
|
||||||
|
note: remove inline-event-listener-panic.html when onerror is implemented properly.
|
||||||
|
|
||||||
|
[Invalid uncompiled raw handlers should only be compiled once.]
|
||||||
|
expected: FAIL
|
|
@ -5874,6 +5874,12 @@
|
||||||
"url": "/_mozilla/mozilla/img_width_height.html"
|
"url": "/_mozilla/mozilla/img_width_height.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"mozilla/inline-event-listener-panic.html": [
|
||||||
|
{
|
||||||
|
"path": "mozilla/inline-event-listener-panic.html",
|
||||||
|
"url": "/_mozilla/mozilla/inline-event-listener-panic.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
"mozilla/inline_event_handler.html": [
|
"mozilla/inline_event_handler.html": [
|
||||||
{
|
{
|
||||||
"path": "mozilla/inline_event_handler.html",
|
"path": "mozilla/inline_event_handler.html",
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Getting the value of an inline event handler shouldn't panic.</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
var e = document.body;
|
||||||
|
e.setAttribute("onclick", "console.log('x')");
|
||||||
|
assert_not_equals(e.onclick, null);
|
||||||
|
assert_not_equals(e.onclick, null);
|
||||||
|
done();
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Inline event handlers retain their ordering even when invalid</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
var events = [];
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
events = [];
|
||||||
|
var e = document.createElement("div");
|
||||||
|
document.body.appendChild(e);
|
||||||
|
e.addEventListener("click", function() { events.push("ONE") });
|
||||||
|
e.setAttribute("onclick", "window.open(");
|
||||||
|
e.addEventListener("click", function() { events.push("THREE") });
|
||||||
|
// Try to compile the event handler.
|
||||||
|
e.onclick;
|
||||||
|
e.setAttribute("onclick", "events.push('TWO')");
|
||||||
|
e.dispatchEvent(new Event("click"));
|
||||||
|
var expected_events = ["ONE", "TWO", "THREE"];
|
||||||
|
assert_array_equals(events, expected_events);
|
||||||
|
}, "Inline event handlers retain their ordering when invalid and force-compiled");
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
events = [];
|
||||||
|
var e = document.createElement("div");
|
||||||
|
document.body.appendChild(e);
|
||||||
|
e.addEventListener("click", function() { events.push("ONE") });
|
||||||
|
e.setAttribute("onclick", "window.open(");
|
||||||
|
e.addEventListener("click", function() { events.push("THREE") });
|
||||||
|
e.dispatchEvent(new Event("click"));
|
||||||
|
e.setAttribute("onclick", "events.push('TWO')");
|
||||||
|
e.dispatchEvent(new Event("click"));
|
||||||
|
var expected_events = ["ONE", "THREE", "ONE", "TWO", "THREE"];
|
||||||
|
assert_array_equals(events, expected_events);
|
||||||
|
}, "Inline event handlers retain their ordering when invalid and force-compiled via dispatch");
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
events = [];
|
||||||
|
var e = document.createElement("div");
|
||||||
|
document.body.appendChild(e);
|
||||||
|
e.addEventListener("click", function() { events.push("ONE") });
|
||||||
|
e.setAttribute("onclick", "window.open(");
|
||||||
|
e.addEventListener("click", function() { events.push("THREE") });
|
||||||
|
e.setAttribute("onclick", "events.push('TWO')");
|
||||||
|
e.dispatchEvent(new Event("click"));
|
||||||
|
var expected_events = ["ONE", "TWO", "THREE"];
|
||||||
|
assert_array_equals(events, expected_events);
|
||||||
|
}, "Inline event handlers retain their ordering when invalid and lazy-compiled");
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Invalid uncompiled raw handlers should only be compiled once.</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
setup({ allow_uncaught_exception: true });
|
||||||
|
|
||||||
|
var errors = 0;
|
||||||
|
window.onerror = function() {
|
||||||
|
errors++;
|
||||||
|
};
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var e = document.body;
|
||||||
|
e.setAttribute("onclick", "window.open(");
|
||||||
|
assert_equals(e.onclick, null);
|
||||||
|
assert_equals(e.onclick, null);
|
||||||
|
assert_equals(errors, 1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
Loading…
Add table
Add a link
Reference in a new issue