mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +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!(usize, u8, u16, u32, u64);
|
||||
|
|
|
@ -11,7 +11,7 @@ use dom::bindings::js::{JS, Root, RootedReference};
|
|||
use dom::bindings::reflector::Reflectable;
|
||||
use dom::bindings::trace::RootedVec;
|
||||
use dom::event::{Event, EventPhase};
|
||||
use dom::eventtarget::{EventListenerType, EventTarget, ListenerPhase};
|
||||
use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
|
||||
use dom::node::Node;
|
||||
use dom::virtualmethods::vtable_for;
|
||||
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) {
|
||||
let _marker;
|
||||
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::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, EventTargetTypeId};
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::reflector::{Reflectable, Reflector};
|
||||
use dom::element::Element;
|
||||
use dom::errorevent::ErrorEvent;
|
||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use dom::eventdispatcher::dispatch_event;
|
||||
use dom::node::document_from_node;
|
||||
use dom::virtualmethods::VirtualMethods;
|
||||
use dom::window::Window;
|
||||
use fnv::FnvHasher;
|
||||
use heapsize::HeapSizeOf;
|
||||
use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue};
|
||||
use js::jsapi::{HandleObject, JSContext, RootedFunction};
|
||||
use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue, RootedFunction};
|
||||
use js::jsapi::{JSAutoCompartment, JSAutoRequest};
|
||||
use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
|
||||
use libc::{c_char, size_t};
|
||||
|
@ -31,6 +33,8 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
|
|||
use std::default::Default;
|
||||
use std::ffi::CString;
|
||||
use std::hash::BuildHasherDefault;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
use std::{intrinsics, ptr};
|
||||
use string_cache::Atom;
|
||||
|
@ -94,10 +98,49 @@ impl EventTargetTypeId {
|
|||
}
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#internal-raw-uncompiled-handler
|
||||
#[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>),
|
||||
Inline(CommonEventHandler),
|
||||
Inline(InlineEventListener),
|
||||
}
|
||||
|
||||
impl HeapSizeOf for EventListenerType {
|
||||
|
@ -108,6 +151,26 @@ impl HeapSizeOf for 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
|
||||
pub fn call_or_handle_event<T: Reflectable>(&self,
|
||||
object: &T,
|
||||
|
@ -115,10 +178,10 @@ impl EventListenerType {
|
|||
exception_handle: ExceptionHandling) {
|
||||
// Step 3
|
||||
match *self {
|
||||
EventListenerType::Additive(ref listener) => {
|
||||
CompiledEventListener::Listener(ref listener) => {
|
||||
let _ = listener.HandleEvent_(object, event, exception_handle);
|
||||
},
|
||||
EventListenerType::Inline(ref handler) => {
|
||||
CompiledEventListener::Handler(ref handler) => {
|
||||
match *handler {
|
||||
CommonEventHandler::ErrorEventHandler(ref handler) => {
|
||||
if let Some(event) = event.downcast::<ErrorEvent>() {
|
||||
|
@ -152,15 +215,61 @@ impl EventListenerType {
|
|||
|
||||
#[derive(JSTraceable, Clone, PartialEq, HeapSizeOf)]
|
||||
#[privatize]
|
||||
pub struct EventListenerEntry {
|
||||
/// A listener in a collection of event listeners.
|
||||
struct EventListenerEntry {
|
||||
phase: ListenerPhase,
|
||||
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]
|
||||
pub struct EventTarget {
|
||||
reflector_: Reflector,
|
||||
handlers: DOMRefCell<HashMap<Atom, Vec<EventListenerEntry>, BuildHasherDefault<FnvHasher>>>,
|
||||
handlers: DOMRefCell<HashMap<Atom, EventListeners, BuildHasherDefault<FnvHasher>>>,
|
||||
}
|
||||
|
||||
impl EventTarget {
|
||||
|
@ -171,17 +280,16 @@ impl EventTarget {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_listeners(&self, type_: &Atom) -> Option<Vec<EventListenerType>> {
|
||||
self.handlers.borrow().get(type_).map(|listeners| {
|
||||
listeners.iter().map(|entry| entry.listener.clone()).collect()
|
||||
pub fn get_listeners(&self, type_: &Atom) -> Option<Vec<CompiledEventListener>> {
|
||||
self.handlers.borrow_mut().get_mut(type_).map(|listeners| {
|
||||
listeners.get_listeners(None, self, type_)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_listeners_for(&self, type_: &Atom, desired_phase: ListenerPhase)
|
||||
-> Option<Vec<EventListenerType>> {
|
||||
self.handlers.borrow().get(type_).map(|listeners| {
|
||||
let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
|
||||
filtered.map(|entry| entry.listener.clone()).collect()
|
||||
-> Option<Vec<CompiledEventListener>> {
|
||||
self.handlers.borrow_mut().get_mut(type_).map(|listeners| {
|
||||
listeners.get_listeners(Some(desired_phase), self, type_)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -195,13 +303,14 @@ impl EventTarget {
|
|||
dispatch_event(self, None, event)
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handlers-11
|
||||
pub fn set_inline_event_listener(&self,
|
||||
ty: Atom,
|
||||
listener: Option<CommonEventHandler>) {
|
||||
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(vec!()),
|
||||
Vacant(entry) => entry.insert(EventListeners(vec!())),
|
||||
};
|
||||
|
||||
let idx = entries.iter().position(|ref entry| {
|
||||
|
@ -213,12 +322,8 @@ impl EventTarget {
|
|||
|
||||
match idx {
|
||||
Some(idx) => {
|
||||
match listener {
|
||||
Some(listener) => entries[idx].listener = EventListenerType::Inline(listener),
|
||||
None => {
|
||||
entries.remove(idx);
|
||||
}
|
||||
}
|
||||
entries[idx].listener =
|
||||
EventListenerType::Inline(listener.unwrap_or(InlineEventListener::Null));
|
||||
}
|
||||
None => {
|
||||
if listener.is_some() {
|
||||
|
@ -231,28 +336,52 @@ impl EventTarget {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
|
||||
let handlers = self.handlers.borrow();
|
||||
let entries = handlers.get(ty);
|
||||
entries.and_then(|entries| entries.iter().filter_map(|entry| {
|
||||
match entry.listener {
|
||||
EventListenerType::Inline(ref handler) => Some(handler.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}).next())
|
||||
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))
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
// https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
|
||||
/// 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,
|
||||
cx: *mut JSContext,
|
||||
url: Url,
|
||||
scope: HandleObject,
|
||||
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
|
||||
url: Url,
|
||||
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)));
|
||||
}
|
||||
|
||||
// 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 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"error\0" as *const u8 as *const c_char];
|
||||
// step 10
|
||||
let is_error = ty == "error" && self.is::<Window>();
|
||||
let is_error = ty == &Atom::from("error") && self.is::<Window>();
|
||||
let args = unsafe {
|
||||
if is_error {
|
||||
&ERROR_ARG_NAMES[..]
|
||||
|
@ -270,12 +399,14 @@ impl EventTarget {
|
|||
}
|
||||
};
|
||||
|
||||
let source: Vec<u16> = source.utf16_units().collect();
|
||||
let options = CompileOptionsWrapper::new(cx, url.as_ptr(), lineno);
|
||||
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 _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 rv = unsafe {
|
||||
CompileFunction(cx,
|
||||
|
@ -284,21 +415,25 @@ impl EventTarget {
|
|||
name.as_ptr(),
|
||||
args.len() as u32,
|
||||
args.as_ptr(),
|
||||
source.as_ptr(),
|
||||
source.len() as size_t,
|
||||
body.as_ptr(),
|
||||
body.len() as size_t,
|
||||
handler.handle_mut())
|
||||
};
|
||||
if !rv || handler.ptr.is_null() {
|
||||
// Step 1.8.2
|
||||
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) };
|
||||
assert!(!funobj.is_null());
|
||||
// Step 1.14
|
||||
if is_error {
|
||||
self.set_error_event_handler(ty, Some(OnErrorEventHandlerNonNull::new(funobj)));
|
||||
Some(CommonEventHandler::ErrorEventHandler(OnErrorEventHandlerNonNull::new(funobj)))
|
||||
} 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>>)
|
||||
{
|
||||
let event_listener = listener.map(|listener|
|
||||
CommonEventHandler::EventHandler(
|
||||
EventHandlerNonNull::new(listener.callback())));
|
||||
InlineEventListener::Compiled(
|
||||
CommonEventHandler::EventHandler(
|
||||
EventHandlerNonNull::new(listener.callback()))));
|
||||
self.set_inline_event_listener(Atom::from(ty), event_listener);
|
||||
}
|
||||
|
||||
|
@ -315,8 +451,9 @@ impl EventTarget {
|
|||
&self, ty: &str, listener: Option<Rc<T>>)
|
||||
{
|
||||
let event_listener = listener.map(|listener|
|
||||
CommonEventHandler::ErrorEventHandler(
|
||||
OnErrorEventHandlerNonNull::new(listener.callback())));
|
||||
InlineEventListener::Compiled(
|
||||
CommonEventHandler::ErrorEventHandler(
|
||||
OnErrorEventHandlerNonNull::new(listener.callback()))));
|
||||
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 entry = match handlers.entry(Atom::from(&*ty)) {
|
||||
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 };
|
||||
|
|
|
@ -162,9 +162,6 @@ impl VirtualMethods for HTMLBodyElement {
|
|||
let do_super_mutate = match (attr.local_name(), mutation) {
|
||||
(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());
|
||||
// https://html.spec.whatwg.org/multipage/
|
||||
// #event-handlers-on-elements,-document-objects,-and-window-objects:event-handlers-3
|
||||
match name {
|
||||
|
@ -175,7 +172,9 @@ impl VirtualMethods for HTMLBodyElement {
|
|||
&atom!("onresize") | &atom!("onunload") | &atom!("onerror")
|
||||
=> {
|
||||
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..],
|
||||
DOMString::from((**attr.value()).to_owned()));
|
||||
false
|
||||
|
|
|
@ -453,12 +453,10 @@ impl VirtualMethods for HTMLElement {
|
|||
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||
match (attr.local_name(), mutation) {
|
||||
(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>();
|
||||
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..],
|
||||
// FIXME(ajeffrey): Convert directly from AttrValue to DOMString
|
||||
DOMString::from(&**attr.value()));
|
||||
|
|
|
@ -33719,6 +33719,18 @@
|
|||
"deleted": [],
|
||||
"items": {
|
||||
"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": [
|
||||
{
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"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": [
|
||||
{
|
||||
"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