From: Jacek Caban Subject: [PATCH 1/4] mshtml: Register load, error and abort events directly in event target. Message-Id: <957d1241-4953-94f3-941e-562bd3d0bf2c@codeweavers.com> Date: Thu, 22 Feb 2018 19:47:26 +0100 Signed-off-by: Jacek Caban --- dlls/mshtml/htmldoc.c | 2 +- dlls/mshtml/htmlelem.c | 11 +--------- dlls/mshtml/htmlevent.c | 53 +++++++++++++++++++++++++++++---------------- dlls/mshtml/htmlevent.h | 2 +- dlls/mshtml/htmlwindow.c | 2 +- dlls/mshtml/tests/events.js | 40 ++++++++++++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 32 deletions(-) diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index 4473b491fd..1f458fc8ae 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -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) diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index 6379101b66..8a4e164540 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -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) diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 6773a535d1..3e0dfa5dba 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -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); + if(!nsnode || nsnode == doc->node.nsnode) { + if(doc->event_vector[eid]) + return S_OK; + doc->event_vector[eid] = TRUE; } - doc->event_vector[eid] = TRUE; add_nsevent_listener(doc, nsnode, event_info[eid].name); - - if(nsnode) - nsIDOMNode_Release(nsnode); 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; } diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index 39bcf01c28..4d1267ed2a 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -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; diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 2ef62cc6c9..80cc7ec3c5 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -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) diff --git a/dlls/mshtml/tests/events.js b/dlls/mshtml/tests/events.js index 7db440aeb8..108afe2481 100644 --- a/dlls/mshtml/tests/events.js +++ b/dlls/mshtml/tests/events.js @@ -743,6 +743,44 @@ function test_keyboard_event() { next_test(); } +function test_error_event() { + document.body.innerHTML = '
'; + 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 ];