mshtml: Register load, error and abort events directly in event target.

Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2018-02-22 19:47:26 +01:00 committed by Alexandre Julliard
parent 9550556f3c
commit d7c94cc7d8
6 changed files with 79 additions and 33 deletions

View File

@ -5048,7 +5048,7 @@ static nsISupports *HTMLDocumentNode_get_gecko_target(DispatchEx *dispex)
static void HTMLDocumentNode_bind_event(DispatchEx *dispex, eventid_t eid)
{
HTMLDocumentNode *This = impl_from_DispatchEx(dispex);
ensure_doc_nsevent_handler(This, eid);
ensure_doc_nsevent_handler(This, This->node.nsnode, eid);
}
static EventTarget *HTMLDocumentNode_get_parent_event_target(DispatchEx *dispex)

View File

@ -5439,16 +5439,7 @@ static nsISupports *HTMLElement_get_gecko_target(DispatchEx *dispex)
static void HTMLElement_bind_event(DispatchEx *dispex, eventid_t eid)
{
HTMLElement *This = impl_from_DispatchEx(dispex);
static const WCHAR loadW[] = {'l','o','a','d',0};
switch(eid) {
case EVENTID_LOAD:
add_nsevent_listener(This->node.doc, This->node.nsnode, loadW);
return;
default:
ensure_doc_nsevent_handler(This->node.doc, eid);
}
ensure_doc_nsevent_handler(This->node.doc, This->node.nsnode, eid);
}
static HRESULT HTMLElement_handle_event_default(DispatchEx *dispex, eventid_t eid, nsIDOMEvent *nsevent, BOOL *prevent_default)

View File

@ -131,11 +131,17 @@ typedef struct {
DWORD flags;
} event_info_t;
/* Use Gecko default listener (it's registered on window object for DOM nodes). */
#define EVENT_DEFAULTLISTENER 0x0001
#define EVENT_BUBBLES 0x0002
#define EVENT_BIND_TO_BODY 0x0008
#define EVENT_CANCELABLE 0x0010
/* Register Gecko listener on target itself (unlike EVENT_DEFAULTLISTENER). */
#define EVENT_BIND_TO_TARGET 0x0002
/* Event bubbles by default (unless explicitly specified otherwise). */
#define EVENT_BUBBLES 0x0004
/* Event is cancelable by default (unless explicitly specified otherwise). */
#define EVENT_CANCELABLE 0x0008
/* Event may have default handler (so we always have to register Gecko listener). */
#define EVENT_HASDEFAULTHANDLERS 0x0020
/* Ecent is not supported properly, print FIXME message when it's used. */
#define EVENT_FIXME 0x0040
/* mouse event flags for fromElement and toElement implementation */
@ -144,7 +150,7 @@ typedef struct {
static const event_info_t event_info[] = {
{abortW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONABORT,
EVENT_BIND_TO_BODY},
EVENT_BIND_TO_TARGET},
{beforeactivateW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONBEFOREACTIVATE,
EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
{beforeunloadW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONBEFOREUNLOAD,
@ -168,7 +174,7 @@ static const event_info_t event_info[] = {
{dragstartW, EVENT_TYPE_DRAG, DISPID_EVMETH_ONDRAGSTART,
EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
{errorW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONERROR,
EVENT_BIND_TO_BODY},
EVENT_BIND_TO_TARGET},
{focusW, EVENT_TYPE_FOCUS, DISPID_EVMETH_ONFOCUS,
EVENT_DEFAULTLISTENER},
{focusinW, EVENT_TYPE_FOCUS, DISPID_EVMETH_ONFOCUSIN,
@ -184,7 +190,7 @@ static const event_info_t event_info[] = {
{keyupW, EVENT_TYPE_KEYBOARD, DISPID_EVMETH_ONKEYUP,
EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
{loadW, EVENT_TYPE_UIEVENT, DISPID_EVMETH_ONLOAD,
EVENT_BIND_TO_BODY},
EVENT_BIND_TO_TARGET},
{messageW, EVENT_TYPE_MESSAGE, DISPID_EVMETH_ONMESSAGE,
0},
{mousedownW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONMOUSEDOWN,
@ -2618,6 +2624,14 @@ static HRESULT dispatch_event_object(EventTarget *event_target, DOMEvent *event,
void dispatch_event(EventTarget *event_target, DOMEvent *event)
{
dispatch_event_object(event_target, event, DISPATCH_BOTH, NULL);
/*
* We may have registered multiple Gecko listeners for the same event type,
* but we already dispatched event to all relevant targets. Stop event
* propagation here to avoid events being dispatched multiple times.
*/
if(event->event_id != EVENTID_LAST && (event_info[event->event_id].flags & EVENT_BIND_TO_TARGET))
nsIDOMEvent_StopPropagation(event->nsevent);
}
HRESULT fire_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_var, VARIANT_BOOL *cancelled)
@ -2679,10 +2693,8 @@ HRESULT fire_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_va
return S_OK;
}
HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, eventid_t eid)
HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, nsIDOMNode *nsnode, eventid_t eid)
{
nsIDOMNode *nsnode = NULL;
TRACE("%s\n", debugstr_w(event_info[eid].name));
if(!doc->nsdoc)
@ -2701,19 +2713,22 @@ HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, eventid_t eid)
break;
}
if(doc->event_vector[eid] || !(event_info[eid].flags & (EVENT_DEFAULTLISTENER|EVENT_BIND_TO_BODY)))
if(event_info[eid].flags & EVENT_DEFAULTLISTENER) {
nsnode = NULL;
}else if(event_info[eid].flags & EVENT_BIND_TO_TARGET) {
if(!nsnode)
nsnode = doc->node.nsnode;
}else {
return S_OK;
if(event_info[eid].flags & EVENT_BIND_TO_BODY) {
nsnode = doc->node.nsnode;
nsIDOMNode_AddRef(nsnode);
}
doc->event_vector[eid] = TRUE;
add_nsevent_listener(doc, nsnode, event_info[eid].name);
if(!nsnode || nsnode == doc->node.nsnode) {
if(doc->event_vector[eid])
return S_OK;
doc->event_vector[eid] = TRUE;
}
if(nsnode)
nsIDOMNode_Release(nsnode);
add_nsevent_listener(doc, nsnode, event_info[eid].name);
return S_OK;
}
@ -2949,7 +2964,7 @@ void update_doc_cp_events(HTMLDocumentNode *doc, cp_static_data_t *cp)
for(i=0; i < EVENTID_LAST; i++) {
if((event_info[i].flags & EVENT_DEFAULTLISTENER) && is_cp_event(cp, event_info[i].dispid))
ensure_doc_nsevent_handler(doc, i);
ensure_doc_nsevent_handler(doc, NULL, i);
}
}
@ -3046,7 +3061,7 @@ HRESULT doc_init_events(HTMLDocumentNode *doc)
for(i=0; i < EVENTID_LAST; i++) {
if(event_info[i].flags & EVENT_HASDEFAULTHANDLERS) {
hres = ensure_doc_nsevent_handler(doc, i);
hres = ensure_doc_nsevent_handler(doc, NULL, i);
if(FAILED(hres))
return hres;
}

View File

@ -98,7 +98,7 @@ HRESULT doc_init_events(HTMLDocumentNode*) DECLSPEC_HIDDEN;
void detach_events(HTMLDocumentNode *doc) DECLSPEC_HIDDEN;
HRESULT create_event_obj(IHTMLEventObj**) DECLSPEC_HIDDEN;
void bind_target_event(HTMLDocumentNode*,EventTarget*,const WCHAR*,IDispatch*) DECLSPEC_HIDDEN;
HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode*,eventid_t) DECLSPEC_HIDDEN;
HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode*,nsIDOMNode*,eventid_t) DECLSPEC_HIDDEN;
void dispatch_event(EventTarget*,DOMEvent*) DECLSPEC_HIDDEN;

View File

@ -3033,7 +3033,7 @@ static nsISupports *HTMLWindow_get_gecko_target(DispatchEx *dispex)
static void HTMLWindow_bind_event(DispatchEx *dispex, eventid_t eid)
{
HTMLInnerWindow *This = impl_from_DispatchEx(dispex);
ensure_doc_nsevent_handler(This->doc, eid);
ensure_doc_nsevent_handler(This->doc, NULL, eid);
}
static void HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode)

View File

@ -743,6 +743,44 @@ function test_keyboard_event() {
next_test();
}
function test_error_event() {
document.body.innerHTML = '<div><img></img></div>';
var div = document.body.firstChild;
var img = div.firstChild;
var calls = "";
function record_call(msg) {
return function() { calls += msg + "," };
}
var win_onerror = record_call("window.onerror");
var doc_onerror = record_call("doc.onerror");
var body_onerror = record_call("body.onerror");
window.addEventListener("error", win_onerror, true);
document.addEventListener("error", doc_onerror, true);
document.body.addEventListener("error", body_onerror, true);
div.addEventListener("error", record_call("div.onerror"), true);
div.addEventListener("error", function() {
ok(calls === "window.onerror,doc.onerror,body.onerror,div.onerror,", "calls = " + calls);
window.removeEventListener("error", win_onerror, true);
document.removeEventListener("error", doc_onerror, true);
document.body.removeEventListener("error", body_onerror, true);
next_test();
}, true);
img.src = "about:blank";
}
function test_detached_img_error_event() {
var img = new Image();
img.onerror = function() {
next_test();
}
img.src = "about:blank";
}
var tests = [
test_content_loaded,
test_add_remove_listener,
@ -758,6 +796,8 @@ var tests = [
test_ui_event,
test_mouse_event,
test_keyboard_event,
test_error_event,
test_detached_img_error_event,
test_time_stamp,
test_listener_order
];